var utility    = require('system/utility'),
	assert     = require('assert'),
	Enumerable = require('collections/enumerable'),
	Linq       = require('collections/linq');

LinqTests = (function () {
	var AsyncEnumerable = function(arr) {
		this._source = arr;
		this.isAsynchronous = true;
	}

	utility.inherits(AsyncEnumerable, Enumerable);

	AsyncEnumerable.prototype.getEnumerator = function() {
		var source = this._source;
		var index = 0;

		return new IEnumerator({
			tryGetNext : function(callback) {
				var result = (index < source.length);
				process.nextTick(function() {
					callback(null, result, result ? source[index++] : undefined);
				});
			}
		});
	}

	var linqDataSets = [
		{
			enumerable  : Enumerable.empty(),
			enumerable2 : Enumerable.empty(),
			result      : [],
			result2     : []
		},
		{
			enumerable  : Enumerable.range(1, 1),
			enumerable2 : Enumerable.empty(),
			result      : [1],
			result2     : []
		},
		{
			enumerable : Enumerable.range(1, 10),
			result : [1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
			enumerable2 : Enumerable.repeat(5).take(7),
			result2 : [5,5,5,5,5,5,5]
		},
		{
			enumerable  : Enumerable.range(1, 3),
			result      : [1, 2, 3],
			enumerable2 : Enumerable.empty(),
			result2     : []
		},
		{
			enumerable  : new AsyncEnumerable([1, 2, 3]),
			result      : [1, 2, 3],
			enumerable2 : new AsyncEnumerable([1, 2, 6, 10]),
			result2     : [1, 2, 6, 10]
		},
		{
			enumerable  : Enumerable.repeat(5).take(7),
			result      : [5,5,5,5,5,5,5],
			enumerable2 : Enumerable.Return(5),
			result2     : [5]
		},
		{
			enumerable  : new AsyncEnumerable([1, 2, 3]),
			result      : [1, 2, 3],
			enumerable2 : Enumerable.range(1, 3),
			result2     : [1, 2, 3]
		}
	];

	var LinqTests = function(dataSets) {
		var runTest = function(doneCallback, callback) {
			var i = 0;

			utility.doWhile(
				function(doCallback) {
					dataSets[i].indexes = [];

					if (!dataSets[i].equal) {
						dataSets[i].equal = function (a, b) { return a === b; };
					}

					if (!dataSets[i].arrayEqual) {
						dataSets[i].arrayEqual = function (a, b) {
							if (a.length != b.length) {
								return false;
							}
						
							for (var i = 0; i < a.length; i++) {
								if (!dataSets[i].equal(a[i], b[i])) {
									return false;
								}
							}

							return true;
						};
					}

					for (var j = 0; j < dataSets[i].result.length; ++j) {
						dataSets[i].indexes.push(j);
					}

					callback(
						function() {
							doCallback(++i < dataSets.length);
						},
						dataSets[i]
					);
				},
				doneCallback
			);
		}

		var getUnique = function(arr) {
			var result = [], found;

			for (var i = 0; i < arr.length; i++) {
				found = false;

				for (var j = 0; j < result.length; j++) {
					if (arr[i] === result[j]) {
						found = true;
						break;
					}
				}

				if (!found) {
					result.push(arr[i]);
				}
			}

			return result;
		}

		var excludeItems = function(arr, arr2) {
			var result = [], found;

			for (var i = 0; i < arr.length; i++) {
				found = false;

				for (var j = 0; j < arr2.length; j++) {
					if (arr[i] === arr2[j]) {
						found = true;
						break;
					}
				}

				if (!found) {
					result.push(arr[i]);
				}
			}

			return result;
		}

		var countOccurences = function(arr, value, fn) {
			var count = 0;

			for (var i = 0; i < arr.length; i++) {
				if ((fn === undefined && arr[i] === value) || (fn !== undefined && fn(value, arr[i]))) {
					++count;
				}
			}

			return count;
		}

		var tests = {};

		if (dataSets == null) {
			utility.extend(
				true,
				tests,
				{
					'Aggregate': {
						'aggregate': function() {
							var actual = Enumerable.range(1, 10).aggregate('a,b=>a+b');
							assert.equal(actual, 55);

							actual = Enumerable.range(1, 10).aggregate(10, 'a,b=>a+b');
							assert.equal(actual, 65);

							actual = Enumerable.range(1, 10).aggregate(10, 'a,b=>a+b', 'val=>val*10');
							assert.equal(actual, 650);
						},
						'average': function() {
							var actual = Enumerable.range(1, 10).average();
							assert.equal(actual, 5.5);

							actual = Enumerable.range(1, 10).select('v,i=>{v:v,i:i}').average('t=>t.i');
							assert.equal(actual, 4.5);
						},
						'count': function() {
							var actual = Enumerable.range(1, 10).count();
							assert.equal(actual, 10);
							actual = Enumerable.empty().count();
							assert.equal(actual, 0);

							actual = Enumerable.range(1, 10).count('i=>i<5');
							assert.equal(actual, 4);
						},
						'max': function() {
							var actual = Enumerable.range(1, 10).max();
							assert.equal(actual, 10);

							actual = Enumerable.range(1, 10).select('v,i=>{v:v,i:i}').max('t=>t.i');
							assert.equal(actual, 9);
						},
						'min': function() {
							var actual = Enumerable.range(1, 10).min();
							assert.equal(actual, 1);

							actual = Enumerable.range(1, 10).select('v,i=>{v:v,i:i}').min('t=>t.i');
							assert.equal(actual, 0);
						},
						'maxBy': function() {
							var actual = Enumerable.range(1, 10).select('v,i=>{v:v,i:i}').maxBy('t=>t.i');
							assert.deepEqual(actual, { v: 10, i: 9 });
						},
						'minBy': function() {
							var actual = Enumerable.range(1, 10).select('v,i=>{v:v,i:i}').minBy('t=>t.i');
							assert.deepEqual(actual, { v: 1, i: 0 });
						},
						'sum': function() {
							var actual = Enumerable.range(1, 10).sum();
							assert.equal(actual, 55);
							actual = Enumerable.empty().sum();
							assert.equal(actual, 0);

							actual = Enumerable.range(1, 10).select('v,i=>{v:v,i:i}').sum('t=>t.i');
							assert.equal(actual, 45);
						}
					},
					'Functional': {
						'let': function() {
							var sum = Enumerable.range(1, 10)
								.let(function (e)
								{
									return e.zip(e, function (a, b) { return { a: a, b: b} });
								})
								.select('$.a + $.b')
								.sum();
							assert.equal(sum, 110);
						},
						'share': function() {
							var share = Enumerable.range(1, 10).share();
							var ar1 = share.take(4).toArray();
							var ar2 = share.toArray();
							var ar3 = share.toArray();
							assert.deepEqual(ar1, [1, 2, 3, 4]);
							assert.deepEqual(ar2, [5, 6, 7, 8, 9, 10]);
							assert.deepEqual(ar3, []);
						},
						'memoizeAll': function() {
							var count = 0;
							var mem = Enumerable.range(1, 5)
								.select(function (x) { count++; return x; })
								.memoizeAll();
							var ar1 = mem.toArray();
							var ar2 = mem.toArray();
							assert.deepEqual(ar1, [1, 2, 3, 4, 5]);
							assert.deepEqual(ar2, [1, 2, 3, 4, 5]);
							assert.equal(5, count);

							mem = [1, 2, undefined, 3, 4].asEnumerable().memoizeAll();

							ar1 = mem.toArray();
							ar2 = mem.toArray();
							assert.deepEqual(ar1, [1, 2, undefined, 3, 4]);
							assert.deepEqual(ar2, [1, 2, undefined, 3, 4]);
						}
					},
					'Grouping': {
						'groupBy': function() {
							var fileList = ['temp.xls', 'temp2.xls', 'temp.pdf', 'temp.jpg', 'temp2.pdf', 'temp3.xls'];

							var actual = fileList
								.asEnumerable()
								.groupBy('file=>file.match(/\\.(.+$)/)[1]')
								.select('{key:$.key,value:$.toArray()}')
								.toArray();
							var expected = [{ key: 'xls', value: ['temp.xls', 'temp2.xls', 'temp3.xls'] },
										{ key: 'pdf', value: ['temp.pdf', 'temp2.pdf'] },
										{ key: 'jpg', value: ['temp.jpg']}];
							assert.deepEqual(actual, expected);

							actual = fileList.asEnumerable()
								.groupBy('file=>file.match(/\\.(.+$)/)[1]', 'file=>file.match(/(^.+)\\..+$/)[1]')
								.select('{key:$.key,value:$.toArray()}')
								.toArray();
							expected = [{ key: 'xls', value: ['temp', 'temp2', 'temp3'] },
										{ key: 'pdf', value: ['temp', 'temp2'] },
										{ key: 'jpg', value: ['temp']}];
							assert.deepEqual(actual, expected);

							actual = fileList.asEnumerable().groupBy('file=>file.match(/\\.(.+$)/)[1]',
								'file=>file',
								'ext,group => {extension:ext,count:group.count(),files:group.toArray()}')
								.toArray();
							expected = [{ extension: 'xls', count: 3, files: ['temp.xls', 'temp2.xls', 'temp3.xls'] },
										{ extension: 'pdf', count: 2, files: ['temp.pdf', 'temp2.pdf'] },
										{ extension: 'jpg', count: 1, files: ['temp.jpg']}];
							assert.deepEqual(actual, expected);

							var objects = [
								{ Date: new Date(2000, 1, 1), Id: 1 },
								{ Date: new Date(2010, 5, 5), Id: 2 },
								{ Date: new Date(2000, 1, 1), Id: 3 }
							]
							actual = objects.asEnumerable()
								.groupBy('$.Date', '$.Id',
									function (key, group) { return key.getFullYear() + '-' + group.toString(',') },
									function (key) { return key.toString() })
								.toArray();
							expected = ['2000-1,3', '2010-2']
							assert.deepEqual(actual, expected);
						},
						'partitionBy': function() {
							var fileList = ['temp.xls', 'temp2.xls', 'temp.pdf', 'temp.jpg', 'temp2.pdf', 'temp3.xls'];

							var actual = fileList
								.asEnumerable()
								.partitionBy('file=>file.match(/\\.(.+$)/)[1]')
								.select('{key:$.key,value:$.toArray()}')
								.toArray();
							var expected = [{ key: 'xls', value: ['temp.xls', 'temp2.xls'] },
										{ key: 'pdf', value: ['temp.pdf'] },
										{ key: 'jpg', value: ['temp.jpg'] },
										{ key: 'pdf', value: ['temp2.pdf'] },
										{ key: 'xls', value: ['temp3.xls'] }
										];
							assert.deepEqual(actual, expected);

							actual = fileList
								.asEnumerable()
								.partitionBy('file=>file.match(/\\.(.+$)/)[1]', 'file=>file.match(/(^.+)\\..+$/)[1]')
								.select('{key:$.key,value:$.toArray()}')
								.toArray();
							expected = [{ key: 'xls', value: ['temp', 'temp2'] },
										{ key: 'pdf', value: ['temp'] },
										{ key: 'jpg', value: ['temp'] },
										{ key: 'pdf', value: ['temp2'] },
										{ key: 'xls', value: ['temp3'] }
										];
							assert.deepEqual(actual, expected);

							actual = fileList
								.asEnumerable()
								.partitionBy('file=>file.match(/\\.(.+$)/)[1]',
									'file=>file',
									'ext,group=>{extension:ext,count:group.count(),files:group.toArray()}')
								.toArray();
							expected = [{ extension: 'xls', count: 2, files: ['temp.xls', 'temp2.xls'] },
										{ extension: 'pdf', count: 1, files: ['temp.pdf'] },
										{ extension: 'jpg', count: 1, files: ['temp.jpg'] },
										{ extension: 'pdf', count: 1, files: ['temp2.pdf'] },
										{ extension: 'xls', count: 1, files: ['temp3.xls'] }
										];
							assert.deepEqual(actual, expected);

							var objects = [
								{ Date: new Date(2000, 1, 1), Id: 1 },
								{ Date: new Date(2000, 1, 1), Id: 2 },
								{ Date: new Date(2010, 5, 5), Id: 3 },
								{ Date: new Date(2000, 1, 1), Id: 4 },
								{ Date: new Date(2010, 5, 5), Id: 5 },
								{ Date: new Date(2010, 5, 5), Id: 6 }
							]
							actual = objects
								.asEnumerable()
								.partitionBy('$.Date', '$.Id',
									function (key, group) { return key.getFullYear() + '-' + group.toString(',') },
									function (key) { return key.toString() })
								.toArray();
							expected = ['2000-1,2', '2010-3', '2000-4', '2010-5,6']
							assert.deepEqual(actual, expected);
						},
						'bufferWithCount': function() {
							var actual = Enumerable.range(1, 10).bufferWithCount('3').toArray();
							var expected = [[1, 2, 3], [4, 5, 6], [7, 8, 9], [10]];
							assert.deepEqual(actual, expected);
						}
					},
					'Join': {
						'join': function() {
							var math    = { yamada: 100, tanaka: 80, yoshida: 94 };
							var english = { yamada: 73, yoshida: 26, tanaka: 99 };
							var actual  = Object.asEnumerable(math)
								.join(english, 'outer=>outer.key', 'inner=>inner.key',
									'o,i=>{Name:o.key,Math:o.value,English:i.value}')
								.toArray();
							var expected = [{ Name: 'yamada', Math: 100, English: 73 },
										{ Name: 'tanaka', Math: 80, English: 99 },
										{ Name: 'yoshida', Math: 94, English: 26}];
							assert.deepEqual(actual, expected);

							actual = Object.asEnumerable(math)
								.join(english, 'outer=>outer', 'inner=>inner',
									'o,i=>{Name:o.key,Math:o.value,English:i.value}', '$.key')
								.toArray();
							expected = [{ Name: 'yamada', Math: 100, English: 73 },
										{ Name: 'tanaka', Math: 80, English: 99 },
										{ Name: 'yoshida', Math: 94, English: 26}];
							assert.deepEqual(actual, expected);
						},
						'groupJoin': function() {
							var array1 = [3, 3, 4, 5, 6];
							var array2 = [2, 4, 5, 6, 6];
							var actual = array1.asEnumerable()
								.groupJoin(array2, ' i => i', ' i => i',
									function (outer, collection)
									{
										return {
											outer: outer,
											collection: collection.toArray()
										}
									})
								.toArray();
							var expected = [{ outer: 3, collection: [] },
								{ outer: 3, collection: [] },
								{ outer: 4, collection: [4] },
								{ outer: 5, collection: [5] },
								{ outer: 6, collection: [6, 6]}];
							assert.deepEqual(actual, expected);

							actual = array1.asEnumerable()
								.groupJoin(array2, ' i => i', ' i => i',
									function (outer, collection)
									{
										return {
											outer: outer,
											collection: collection.toArray()
										}
									},
									function (key) { return key % 2 == 0; })
								.toArray();
							expected = [{ outer: 3, collection: [5] },
										{ outer: 3, collection: [5] },
										{ outer: 4, collection: [2, 4, 6, 6] },
										{ outer: 5, collection: [5] },
										{ outer: 6, collection: [2, 4, 6, 6]}];
							assert.deepEqual(actual, expected);
						}
					},
					'Ordering': {
						'orderBy': function() {
							var actual = [1, 51, 7, 823, 85, 31, 51, 99]
								.asEnumerable()
								.orderBy('i=>i')
								.toArray();
							assert.deepEqual(actual, [1, 7, 31, 51, 51, 85, 99, 823]);
						},
						'orderByDescending': function() {
							var actual = [1, 51, 7, 823, 85, 31, 51, 99]
								.asEnumerable()
								.orderByDescending('i=>i')
								.toArray();
							assert.deepEqual(actual, [823, 99, 85, 51, 51, 31, 7, 1]);
						},
						'thenBy': function() {
							var strlist = [
								{ a: 'a', b: 'z', c: 'b' },
								{ a: 'z', b: 'e', c: 'e' },
								{ a: 'n', b: 'd', c: 'q' },
								{ a: 'a', b: 'c', c: 'k' },
								{ a: 'n', b: 'd', c: 'o' }
							];

							var list = [
								{ a: 2, b: 4, c: 1 },
								{ a: 2, b: 3, c: 7 },
								{ a: 6, b: 6, c: 3 },
								{ a: 4, b: 4, c: 5 },
								{ a: 7, b: 3, c: 2 },
								{ a: 4, b: 4, c: 3 }
							];

							var actual = list
								.asEnumerable()
								.orderBy('l=>l.a')
								.thenBy('l=>l.b')
								.thenBy('l=>l.c')
								.toArray();
							var expected = [
								{ a: 2, b: 3, c: 7 },
								{ a: 2, b: 4, c: 1 },
								{ a: 4, b: 4, c: 3 },
								{ a: 4, b: 4, c: 5 },
								{ a: 6, b: 6, c: 3 },
								{ a: 7, b: 3, c: 2 }
							];
							assert.deepEqual(actual, expected);

							actual = strlist
								.asEnumerable()
								.orderBy('l=>l.a')
								.thenBy('l=>l.b')
								.thenBy('l=>l.c')
								.toArray();
							expected = [
								{ a: 'a', b: 'c', c: 'k' },
								{ a: 'a', b: 'z', c: 'b' },
								{ a: 'n', b: 'd', c: 'o' },
								{ a: 'n', b: 'd', c: 'q' },
								{ a: 'z', b: 'e', c: 'e' }
							];
							assert.deepEqual(actual, expected);
						},
						'thenByDescending': function() {
							var strlist = [
								{ a: 'a', b: 'z', c: 'b' },
								{ a: 'z', b: 'e', c: 'e' },
								{ a: 'n', b: 'd', c: 'q' },
								{ a: 'a', b: 'c', c: 'k' },
								{ a: 'n', b: 'd', c: 'o' }
							];

							var list = [
								{ a: 2, b: 4, c: 1 },
								{ a: 2, b: 3, c: 7 },
								{ a: 6, b: 6, c: 3 },
								{ a: 4, b: 4, c: 5 },
								{ a: 7, b: 3, c: 2 },
								{ a: 4, b: 4, c: 3 }
							];

							var actual = list
								.asEnumerable()
								.orderByDescending('l=>l.a')
								.thenByDescending('l=>l.b')
								.thenByDescending('l=>l.c')
								.toArray();
							var expected = [
								{ a: 7, b: 3, c: 2 },
								{ a: 6, b: 6, c: 3 },
								{ a: 4, b: 4, c: 5 },
								{ a: 4, b: 4, c: 3 },
								{ a: 2, b: 4, c: 1 },
								{ a: 2, b: 3, c: 7 }
							];
							assert.deepEqual(actual, expected);

							actual = strlist
								.asEnumerable()
								.orderByDescending('l=>l.a')
								.thenByDescending('l=>l.b')
								.thenByDescending('l=>l.c')
								.toArray();
							expected = [
								{ a: 'z', b: 'e', c: 'e' },
								{ a: 'n', b: 'd', c: 'q' },
								{ a: 'n', b: 'd', c: 'o' },
								{ a: 'a', b: 'z', c: 'b' },
								{ a: 'a', b: 'c', c: 'k' }
							];
							assert.deepEqual(actual, expected);
						},
						'reverse': function() {
							var actual = [1, 51, 7, 823, 85, 31, 51, 99]
								.asEnumerable()
								.reverse()
								.toArray();
							assert.deepEqual(actual, [99, 51, 31, 85, 823, 7, 51, 1]);
						},
						'shuffle': function() {
							var array = [1, 51, 7, 823, 85, 31, 51, 99];
							var shuffled = array.asEnumerable().shuffle().toArray();
							assert.notDeepEqual(shuffled, array, 'random test. if failed retry');
						}
					},
					'Projection': {
						'cascadeDepthFirst': function() {
							var actual = Enumerable.Return(1).cascadeDepthFirst('$+$').take(7).toArray();
							assert.deepEqual(actual, [1, 2, 4, 8, 16, 32, 64]);
							actual = Enumerable.Return(1).cascadeDepthFirst('$+$', 'v,nl=>{v:v,nl:nl}').take(3).toArray();
							assert.deepEqual(actual, [{ v: 1, nl: 0 }, { v: 2, nl: 1 }, { v: 4, nl: 2}]);
						},
						'cascadeBreadthFirst': function() {
							var actual = Enumerable.Return(1).cascadeBreadthFirst('$+$').take(7).toArray();
							assert.deepEqual(actual, [1, 2, 4, 8, 16, 32, 64]);
							actual = Enumerable.Return(1).cascadeBreadthFirst('$+$', 'v,nl=>{v:v,nl:nl}').take(3).toArray();
							assert.deepEqual(actual, [{ v: 1, nl: 0 }, { v: 2, nl: 1 }, { v: 4, nl: 2}]);
						},
						'flatten': function() {
							var array = [1, 31, [431, 41, 5], [1431, 43, [344, 3], 43], 43];
							var actual = array.asEnumerable().flatten().toArray();
							assert.deepEqual(actual, [1, 31, 431, 41, 5, 1431, 43, 344, 3, 43, 43]);
						},
						'pairwise': function() {
							var actual = Enumerable.range(1, 4).pairwise('prev,next=>{p:prev,n:next}').toArray();
							assert.deepEqual(actual, [{ p: 1, n: 2 }, { p: 2, n: 3 }, { p: 3, n: 4}]);
						},
						'scan': function() {
							var actual = Enumerable.range(1, 10).scan('a,b=>a+b').toArray();
							assert.deepEqual(actual, [1, 3, 6, 10, 15, 21, 28, 36, 45, 55]);
							var seed = 100;
							actual = Enumerable.range(1, 10).scan(seed, 'a,b=>a+b').toArray();
							assert.deepEqual(actual, [100, 101, 103, 106, 110, 115, 121, 128, 136, 145, 155]);
							actual = Enumerable.range(1, 10).scan(seed, 'a,b=>a+b', 'val=>val*10').toArray();
							assert.deepEqual(actual, [1000, 1010, 1030, 1060, 1100, 1150, 1210, 1280, 1360, 1450, 1550]);
						},
						'selectMany': function() {
							var actual = Enumerable.range(1, 5).selectMany('i=>Enumerable.repeat(i,2)').toArray();
							assert.deepEqual(actual, [1, 1, 2, 2, 3, 3, 4, 4, 5, 5]);
							actual = Enumerable.range(1, 5).selectMany('i,index=>Enumerable.repeat(i,index+1)').toArray();
							assert.deepEqual(actual, [1, 2, 2, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 5]);
							actual = Enumerable.range(1, 5).selectMany('i=>Enumerable.repeat(i,2)', 'i=>i*10').toArray();
							assert.deepEqual(actual, [10, 10, 20, 20, 30, 30, 40, 40, 50, 50]);
							actual = Enumerable.range(1, 5).selectMany('i,index=>Enumerable.repeat(i,index+1)', 'i=>i*10').toArray();
							assert.deepEqual(actual, [10, 20, 20, 30, 30, 30, 40, 40, 40, 40, 50, 50, 50, 50, 50]);
						},
						'where': function() {
							var actual = Enumerable.range(1, 10).where('i=>i%2==0').toArray();
							assert.deepEqual(actual, [2, 4, 6, 8, 10]);
							actual = Enumerable.range(1, 10).where('i,index=>(i+index)%3==0').toArray();
							assert.deepEqual(actual, [2, 5, 8]);
						},
						'ofType': function() {
							var seq = [1, 2, 'hoge', '3', 4, true].asEnumerable();
							assert.deepEqual(seq.ofType(Number).toArray(), [1, 2, 4]);
							assert.deepEqual(seq.ofType(String).toArray(), ['hoge', '3']);
							assert.deepEqual(seq.ofType(Boolean).toArray(), [true]);

							var Cls = function (val) { this.val = val; }
							seq = [new Cls('a'), new Cls('b'), 1, 2, new Cls('c'), 3].asEnumerable();
							assert.deepEqual(seq.ofType(Cls).select('$.val').toArray(), ['a', 'b', 'c']);
						},
						'zip': function() {
							var actual = Enumerable.range(1, 10).zip(Enumerable.range(20, 5), 'outer,inner=>outer+inner').toArray();
							assert.deepEqual(actual, [21, 23, 25, 27, 29]);
							actual = Enumerable.range(1, 10).zip(Enumerable.range(20, 5), 'outer,inner,index=>outer+inner+index').toArray();
							assert.deepEqual(actual, [21, 24, 27, 30, 33]);
						}
					}
				}
			);

			dataSets = linqDataSets;
		}

		utility.extend(
			true,
			tests,
			{
				'Action': {
					'#Do(action<element,index>)': {
						'an action callback should pass the current item as the first argument' : function(done) {
							runTest(done, function(callback, settings) {
								var array  = [];
								var actual = 
									settings.enumerable
										.Do(function(i) { 
											array.push(i);
										})
										.force(function() {
											assert.deepEqual(array, settings.result);

											if (settings.enumerable.isAsynchronous) { callback(true); }
										});

								assert.deepEqual(actual, undefined);
								assert.deepEqual(array, settings.enumerable.isAsynchronous ? [] : settings.result);

								if (!settings.enumerable.isAsynchronous) { callback(true); }
							});
						},
						'an action callback should pass the current item as the first argument and the index as the second' : function(done) {
							runTest(done, function(callback, settings) {
								var array  = [];
								var array2 = []
								var actual = 
									settings.enumerable
										.Do(function(v, i) { 
											array.push(v);
											array2.push(i);
										})
										.force(function() {
											assert.deepEqual(array, settings.result);
											assert.deepEqual(array2, settings.indexes);

											if (settings.enumerable.isAsynchronous) { callback(true); }
										});

								assert.equal(actual, undefined);
								assert.deepEqual(array, settings.enumerable.isAsynchronous ? [] : settings.result);
								assert.deepEqual(array2, settings.enumerable.isAsynchronous ? [] : settings.indexes);

								if (!settings.enumerable.isAsynchronous) { callback(true); }
							});
						}
					},
					'#forEach(action<element,index>, [doneCallback<err>])': {
						'an action callback should pass the current item as the first argument' : function(done) {
							runTest(done, function(callback, settings) {
								var finallyCount = 0;
								var array        = [];
								var actual       = 
									settings.enumerable
										.forEach(
											function (i) { array.push(i) },
											function() {
												finallyCount++;
												assert.deepEqual(array, settings.result);

												if (settings.enumerable.isAsynchronous) {
													setTimeout(function() {
														assert(finallyCount, 1);
														callback(true);
													}, 10);
												}
											}
										);

								assert.equal(actual, undefined);
								assert.deepEqual(array, settings.enumerable.isAsynchronous ? [] : settings.result);
								assert.equal(finallyCount, settings.enumerable.isAsynchronous ? 0 : 1);	

								if (!settings.enumerable.isAsynchronous) { callback(true); }
							});
						},
						'an action callback should pass the current item as the first argument and the index as the second' : function(done) {
							runTest(done, function(callback, settings) {
								var finallyCount = 0;
								var array        = [];
								var array2       = []
								var actual       = 
									settings.enumerable
										.forEach(
											function(v, i) { 
												array.push(v);
												array2.push(i);
											},
											function() {
												finallyCount++;
												assert.deepEqual(array, settings.result);
												assert.deepEqual(array2, settings.indexes);

												if (settings.enumerable.isAsynchronous) {
													setTimeout(function() {
														assert(finallyCount, 1);
														callback(true);
													}, 10);
												}
											}
										);

								assert.equal(actual, undefined);
								assert.deepEqual(array, settings.enumerable.isAsynchronous ? [] : settings.result);
								assert.deepEqual(array2, settings.enumerable.isAsynchronous ? [] : settings.indexes);
								assert.equal(finallyCount, settings.enumerable.isAsynchronous ? 0 : 1);	

								if (!settings.enumerable.isAsynchronous) { callback(true); }
							});
						},
						'returning false from an action callback should abort the forEach' : function(done) {
							runTest(done, function(callback, settings) {
								utility.do(
									function() {
										var finallyCount = 0;
										var next         = this.parallel();
										var array        = [];
										var index        = 0;
										var actual       = settings.enumerable
											.forEach(
												function(i) { if (index++ === 4) return false; array.push(i); },
												function(err) {
													finallyCount++;
													assert.equal(err, undefined);
													assert.deepEqual(array, settings.result.slice(0, 4));
															
													if (settings.enumerable.isAsynchronous) {
														setTimeout(function() {
															assert(finallyCount, 1);
															next();
														}, 10);
													}
												}
											);

										assert.equal(actual, undefined);
										assert.deepEqual(array, settings.enumerable.isAsynchronous ? [] : settings.result.slice(0, 4));
										assert.equal(finallyCount, settings.enumerable.isAsynchronous ? 0 : 1);	

										if (!settings.enumerable.isAsynchronous) { next(); }
									},
									function() {
										var finallyCount = 0;
										var next         = this.parallel();
										var array        = [];
										var array2       = [];
										var actual       = settings.enumerable
											.forEach(
												function(v, i) { if (i === 4) return false; array.push(v); array2.push(i); },
												function() {
													finallyCount++;
													assert.deepEqual(array, settings.result.slice(0, 4));
													assert.deepEqual(array2, settings.indexes.slice(0, 4));
															
													if (settings.enumerable.isAsynchronous) {
														setTimeout(function() {
															assert(finallyCount, 1);
															next();
														}, 10);
													}
												}
											);

										assert.equal(actual, undefined);
										assert.deepEqual(array, settings.enumerable.isAsynchronous ? [] : settings.result.slice(0, 4));
										assert.deepEqual(array2, settings.enumerable.isAsynchronous ? [] : settings.indexes.slice(0, 4));
										assert.equal(finallyCount, settings.enumerable.isAsynchronous ? 0 : 1);

										if (!settings.enumerable.isAsynchronous) { next(); }
									},
									callback
								);
							});
						},
					},
					'#force([doneCallback<err>])': {
						'force enumerate through all the items' : function(done) {
							runTest(done, function(callback, settings) {
								var forceCount = 0;
								var array      = [];
								var actual     =
									settings.enumerable
										.Do(function (i) { 
											array.push(i);
										})
										.force(function(err) {
											forceCount++;
											assert.equal(err, undefined);
											assert.deepEqual(array, settings.result);

											if (settings.enumerable.isAsynchronous) {
												setTimeout(function() {
													assert(forceCount, 1);
													callback(true);
												}, 10);
											}
										});

								assert.equal(actual, undefined);
								assert.deepEqual(array, settings.enumerable.isAsynchronous ? [] : settings.result);
								assert.equal(forceCount, settings.enumerable.isAsynchronous ? 0 : 1);

								if (!settings.enumerable.isAsynchronous) { callback(true); }
							});
						}
					}
				},
				'Convert': {
					'#toArray([doneCallback<err, result>])': {
						'toArray should return an array representation of the enumerable' : function(done) {
							runTest(done, function(callback, settings) {
								var toArrayCount = 0;
								var actual       = settings.enumerable.toArray(function(err, arr) {
									toArrayCount++;

									assert.deepEqual(arr, settings.result);

									if (settings.enumerable.isAsynchronous) {
										setTimeout(function() {
											assert(toArrayCount, 1);
											callback(true);
										}, 10);
									}
								});

								assert.deepEqual(actual, settings.enumerable.isAsynchronous ? undefined : settings.result);
								assert.equal(toArrayCount, settings.enumerable.isAsynchronous ? 0 : 1);

								if (!settings.enumerable.isAsynchronous) {
									actual = settings.enumerable.toArray();
											
									assert.deepEqual(actual, settings.result);
									callback(true);
								}
							});
						}
					},
					'#toLookup(keySelector, elementSelector, [compareSelector], [doneCallback<err, result>])': function(done) {
						runTest(done, function(callback, settings) {
							var fileList = ['temp.xls', 'temp2.xls', 'temp.pdf', 'temp.jpg', 'temp2.pdf'];
							var actual   = fileList.asEnumerable().toLookup('file=>file.match(/\\.(.+$)/)[1]');

							assert.deepEqual(['temp.xls', 'temp2.xls'], actual.get('xls'));
							assert.deepEqual(['temp.pdf', 'temp2.pdf'], actual.get('pdf'));
							assert.deepEqual(['temp.jpg'], actual.get('jpg'));
							assert.equal(3, actual.count());
							assert.ok(actual.contains('xls'));
							assert.ok(!actual.contains('XLS'));
							var array = actual.toArray();
							assert.equal('xls', array[0].key);
							assert.deepEqual(['temp.xls', 'temp2.xls'], array[0].toArray());

							actual = fileList.asEnumerable().toLookup('file=>file.match(/\\.(.+$)/)[1]', 'file=>file +"ele"');
							assert.deepEqual(['temp.xlsele', 'temp2.xlsele'], actual.get('xls'));
							assert.deepEqual(['temp.pdfele', 'temp2.pdfele'], actual.get('pdf'));
							assert.deepEqual(['temp.jpgele'], actual.get('jpg'));

							fileList = ['temp.xls', 'temp2.XLS', 'temp.pdf', 'temp.jpg', 'temp2.pDf'];
							actual = fileList.asEnumerable().toLookup('file=>file.match(/\\.(.+$)/)[1]', 'file=>file +"ele"',
								function (s) { return s.toLowerCase() });
							assert.deepEqual(actual.get('xLS'), ['temp.xlsele', 'temp2.XLSele']);
							assert.deepEqual(actual.get('PDf'), ['temp.pdfele', 'temp2.pDfele']);
							assert.deepEqual(actual.get('Jpg'), ['temp.jpgele']);
							assert.ok(actual.contains('xls'));
							assert.ok(actual.contains('XLS'));
							callback(true);
						});
					},
					'#toObject(keySelector, elementSelector, [doneCallback<err, result>])': {
						'if a key selector is not specified it should return an object with the value as the key' : function(done) {
							runTest(done, function(callback, settings) {
								var index     = 0;
								var objResult = {};

								for (var i = 0; i < settings.result.length; ++i) {
									objResult[settings.result[i]] = settings.result[i];
								}

								var actual = settings.enumerable.toObject(undefined, undefined, function(err, value) {
									assert.equal(err, undefined);
									assert.deepEqual(value, objResult);
													
									if (settings.enumerable.isAsynchronous) {
										callback(true);
									}
								});

								assert.deepEqual(actual, settings.enumerable.isAsynchronous ? undefined : objResult);

								if (!settings.enumerable.isAsynchronous) {
									actual = settings.enumerable.toObject();

									assert.deepEqual(actual, objResult);
									callback(true);
								}
							});
						},
						'if a key selector is specified it should alter the keys in the resulting object' : function(done) {
							runTest(done, function(callback, settings) {
								var index     = 0;
								var objResult = {};

								for (var i = 0; i < settings.result.length; ++i) {
									objResult['foo' + i] = settings.result[i];
								}

								var actual = settings.enumerable.toObject(function(i) { return 'foo' + index++; }, undefined, function(err, value) {
									assert.equal(err, undefined);
									assert.deepEqual(value, objResult);
													
									if (settings.enumerable.isAsynchronous) {
										callback(true);
									}
								});

								assert.deepEqual(actual, settings.enumerable.isAsynchronous ? undefined : objResult);

								if (!settings.enumerable.isAsynchronous) {
									index  = 0;
									actual = settings.enumerable.toObject(function(i) { return 'foo' + index++; });

									assert.deepEqual(actual, objResult);
									callback(true);
								}
							});
						},
						'if a value selector is specified it should alter the values in the resulting object': function(done) {
							runTest(done, function(callback, settings) {
								var index     = 0;
								var objResult = {};

								for (var i = 0; i < settings.result.length; ++i) {
									objResult['foo' + i] = { 'bar' : settings.result[i] };
								}

								var actual = settings.enumerable.toObject(function(i) { return 'foo' + index++; }, function(i) { return { 'bar' : settings.result[index-1] }; }, function(err, value) {
									assert.equal(err, undefined);
									assert.deepEqual(value, objResult);
													
									if (settings.enumerable.isAsynchronous) {
										callback(true);
									}
								});

								assert.deepEqual(actual, settings.enumerable.isAsynchronous ? undefined : objResult);

								if (!settings.enumerable.isAsynchronous) {
									index  = 0;
									actual = settings.enumerable.toObject(function(i) { return 'foo' + index++; }, function(i) { return { 'bar' : settings.result[index-1] }; });

									assert.deepEqual(actual, objResult);
									callback(true);
								}
							});
						}
					},
					'#toDictionary(keySelector, elementSelector, [compareSelector], [doneCallback<err, result>])': {
						'if a key selector is not specified it should return an object with the index+1 as the key' : function(done) {
							runTest(done, function(callback, settings) {
								var unique = getUnique(settings.result);
								var actual = settings.enumerable.toDictionary(undefined, undefined, undefined, function(err, value) {
									assert.equal(err, undefined);
									assert.equal(value.length, unique.length);
											
									for (var i = 0; i < unique.length; i++) {
										assert.equal(value.get(unique[i]), unique[i]);
									}

									if (settings.enumerable.isAsynchronous) {
										callback(true);
									}
								});

								if (settings.enumerable.isAsynchronous) {
									assert.deepEqual(actual, undefined);
								} else {
									assert.equal(actual.length, unique.length);

									for (var i = 0; i < unique.length; i++) {
										assert.equal(actual.get(unique[i]), unique[i]);
									}
								}

								if (!settings.enumerable.isAsynchronous) {
									actual = settings.enumerable.toDictionary();

									assert.equal(actual.length, unique.length);

									for (var i = 0; i < unique.length; i++) {
										assert.equal(actual.get(unique[i]), unique[i]);
									}

									callback(true);
								}
							});
						},
						'if a key selector is specified it should alter the keys in the resulting dictionary' : function(done) {
							runTest(done, function(callback, settings) {
								var index = 0;
								var actual = settings.enumerable.toDictionary(function(i) { return 'foo' + index++; }, undefined, undefined, function(err, value) {
									assert.equal(err, undefined);
									assert.equal(value.length, settings.result.length);
											
									for (var i = 0; i < settings.result.length; i++) {
										assert.deepEqual(value.get('foo' + i), settings.result[i]);
									}

									if (settings.enumerable.isAsynchronous) {
										callback(true);
									}
								});

								if (settings.enumerable.isAsynchronous) {
									assert.deepEqual(actual, undefined);
								} else {
									assert.equal(actual.length, settings.result.length);

									for (var i = 0; i < settings.result.length; i++) {
										assert.deepEqual(actual.get('foo' + i), settings.result[i]);
									}
								}

								if (!settings.enumerable.isAsynchronous) {
									index  = 0;
									actual = settings.enumerable.toDictionary(function(i) { return 'foo' + index++; });

									assert.equal(actual.length, settings.result.length);

									for (var i = 0; i < settings.result.length; i++) {
										assert.deepEqual(actual.get('foo' + i), settings.result[i]);
									}

									callback(true);
								}
							});
						},
						'if a value selector is specified it should alter the values in the resulting dictionary' : function(done) {
							runTest(done, function(callback, settings) {
								var unique = getUnique(settings.result);
								var index  = 0;
								var actual = settings.enumerable.toDictionary(undefined, function(i) { return 'foo' + index++; }, undefined, function(err, value) {
									assert.equal(err, undefined);
									assert.equal(value.length, unique.length);
											
									for (var i = 0; i < unique.length; i++) {
										assert.equal(value.get(unique[i]), 'foo' + settings.result.lastIndexOf(unique[i]));
									}

									if (settings.enumerable.isAsynchronous) {
										callback(true);
									}
								});

								if (settings.enumerable.isAsynchronous) {
									assert.deepEqual(actual, undefined);
								} else {
									assert.equal(actual.length, unique.length);

									for (var i = 0; i < unique.length; i++) {
										assert.equal(actual.get(unique[i]), 'foo' + settings.result.lastIndexOf(unique[i]));
									}
								}

								if (!settings.enumerable.isAsynchronous) {
									index  = 0;
									actual = settings.enumerable.toDictionary(undefined, function(i) { return 'foo' + index++; });

									assert.equal(actual.length, unique.length);

									for (var i = 0; i < unique.length; i++) {
										assert.equal(actual.get(unique[i]), 'foo' + settings.result.lastIndexOf(unique[i]));
									}

									callback(true);
								}
							});
						},
						'if a compare selector is specified it should alter the resulting dictionary' : function(done) {
							runTest(done, function(callback, settings) {
								var unique = getUnique(settings.result);
								var index  = 0;
								var actual = settings.enumerable.toDictionary('i=>{key:i,V:"foo"+i}', 'i=>i', '$.key', function(err, value) {
									assert.equal(err, undefined);
									assert.equal(value.length, unique.length);
											
									for (var i = 0; i < unique.length; i++) {
										assert.equal(value.get({ key: unique[i] }), unique[i]);
									}

									if (settings.enumerable.isAsynchronous) {
										callback(true);
									}
								});

								if (settings.enumerable.isAsynchronous) {
									assert.deepEqual(actual, undefined);
								} else {
									assert.equal(actual.length, unique.length);

									for (var i = 0; i < unique.length; i++) {
										assert.equal(actual.get({ key: unique[i] }), unique[i]);
									}
								}

								if (!settings.enumerable.isAsynchronous) {
									index  = 0;
									actual = settings.enumerable.toDictionary('i=>{key:i,V:"foo"+i}', 'i=>i', '$.key');

									assert.equal(actual.length, unique.length);

									for (var i = 0; i < unique.length; i++) {
										assert.equal(actual.get({ key: unique[i] }), unique[i]);
									}

									callback(true);
								}
							});
						}
					},
					'#toString([separator], [selector], [doneCallback<err, result>])': {
						'it should return a string representation of all the items' : function(done) {
							runTest(done, function(callback, settings) {
								var strResult = '';

								for (var i = 0; i < settings.result.length; ++i) {
									if (settings.result[i]['toString'] instanceof Function) {
										strResult += settings.result[i].toString();
									} else {
										strResult += settings.result[i];
									}
								}

								var actual = settings.enumerable.toString(undefined, undefined, function(err, value) {
									assert.equal(err, undefined);
									assert.equal(value, strResult);
													
									if (settings.enumerable.isAsynchronous) {
										callback(true);
									}
								});

								assert.equal(actual, settings.enumerable.isAsynchronous ? undefined : strResult);

								if (!settings.enumerable.isAsynchronous) {
									actual = settings.enumerable.toString();

									assert.equal(actual, strResult);
									callback(true);
								}
							});
						},
						'it should return a string representation of all the items with a seperator inbetween' : function(done) {
							runTest(done, function(callback, settings) {
								var strResult = '';

								for (var i = 0; i < settings.result.length; ++i) {
									if (settings.result[i]['toString'] instanceof Function) {
										strResult += settings.result[i].toString();
									} else {
										strResult += settings.result[i];
									}

									if (i < settings.result.length - 1) { strResult += '-'; }
								}

								var actual = settings.enumerable.toString('-', undefined, function(err, value) {
									assert.equal(err, undefined);
									assert.equal(value, strResult);
													
									if (settings.enumerable.isAsynchronous) {
										callback(true);
									}
								});

								assert.equal(actual, settings.enumerable.isAsynchronous ? undefined : strResult);

								if (!settings.enumerable.isAsynchronous) {
									actual = settings.enumerable.toString('-');

									assert.equal(actual, strResult);
									callback(true);
								}
							});
						},
						'specifying a selector should return the selector items in the resulting string' : function(done) {
							runTest(done, function(callback, settings) {
								var strResult = '';

								for (var i = 0; i < settings.result.length; ++i) {
									if (settings.result[i]['toString'] instanceof Function) {
										strResult += 'f' + settings.result[i].toString() + 'i';
									} else {
										strResult += 'f' + settings.result[i] + 'i';
									}

									if (i < settings.result.length - 1) { strResult += '-'; }
								}

								var actual = settings.enumerable.toString('-', 'i=>"f"+i+"i"', function(err, value) {
									assert.equal(err, undefined);
									assert.equal(value, strResult);
													
									if (settings.enumerable.isAsynchronous) {
										callback(true);
									}
								});

								assert.equal(actual, settings.enumerable.isAsynchronous ? undefined : strResult);

								if (!settings.enumerable.isAsynchronous) {
									actual = settings.enumerable.toString('-', 'i=>"f"+i+"i"');

									assert.equal(actual, strResult);
									callback(true);
								}
							});
						},
					},
					'#toJSON([replacer], [space], [doneCallback<err, result>])': {
						'specifying a no arguments should just convert the enumerable to an array' : function(done) {
							runTest(done, function(callback, settings) {
								var strResult = JSON.stringify(settings.result);
								var actual = settings.enumerable.toJSON(undefined, undefined, function(err, value) {
									assert.equal(err, undefined);
									assert.equal(value, strResult);
													
									if (settings.enumerable.isAsynchronous) {
										callback(true);
									}
								});

								assert.equal(actual, settings.enumerable.isAsynchronous ? undefined : strResult);

								if (!settings.enumerable.isAsynchronous) {
									actual = settings.enumerable.toJSON();

									assert.equal(actual, strResult);
									callback(true);
								}
							});
						},
						'specifying a replacer should alter the result' : function(done) {
							runTest(done, function(callback, settings) {
								var strResult = JSON.stringify(settings.result, function(k,v) { return 1; });

								var actual = settings.enumerable.toJSON(function(k,v) { return 1; }, undefined, function(err, value) {
									assert.equal(err, undefined);
									assert.equal(value, strResult);
													
									if (settings.enumerable.isAsynchronous) {
										callback(true);
									}
								});

								assert.equal(actual, settings.enumerable.isAsynchronous ? undefined : strResult);

								if (!settings.enumerable.isAsynchronous) {
									actual = settings.enumerable.toJSON(function(k,v) { return 1; });

									assert.equal(actual, strResult);
									callback(true);
								}
							});
						},
						'specifying space should add space to be beginning' : function(done) {
							runTest(done, function(callback, settings) {
								if (settings.result.length < 1) {
									callback(false);
									return;
								}

								var actual = settings.enumerable.toJSON(undefined, 1, function(err, value) {
									assert.equal(err, undefined);
									assert.ok(value.indexOf('\n') != -1);
													
									if (settings.enumerable.isAsynchronous) {
										callback(true);
									}
								});

								if (settings.enumerable.isAsynchronous) {
									assert.equal(undefined);
								} else {
									assert.ok(actual.indexOf('\n') != -1);
								}

								if (!settings.enumerable.isAsynchronous) {
									actual = settings.enumerable.toJSON(undefined, 1);

									assert.ok(actual.indexOf('\n') != -1);
									callback(true);
								}
							});
						}
					}
				},
				'Error Handling': {
					'#Catch(handler<ex>)': {
						'catch should be called when an exception is thrown' : function(done) {
// BAD TEST: figure out how to make catch the first call off of enumerable
							runTest(done, function(callback, settings) {
								var msg    = '';
								var actual = settings.enumerable
									.select(function(v,i)
									{
										if (i == 4) throw new Error('aiueo');
										return v;
									})
									.Catch(function(e)
									{
										msg += e.message;
									})
									.toArray(function(err, arr) {
										assert.equal(err, undefined);
										assert.deepEqual(arr, settings.result.slice(0, 4));
										assert.equal(msg, (settings.result.length <= 4) ? '' : 'aiueo');

										if (settings.enumerable.isAsynchronous) {
											callback(true);
										}
									});

								assert.deepEqual(actual, settings.enumerable.isAsynchronous ? undefined : settings.result.slice(0, 4));
								assert.equal(msg, (settings.result.length <= 4 || settings.enumerable.isAsynchronous) ? '' : 'aiueo');

								if (!settings.enumerable.isAsynchronous) { callback(true); }
							});
						},
						'catch should not be called when an exception is not thrown' : function(done) {
							runTest(done, function(callback, settings) {
								var msg    = '';
								var actual = settings.enumerable
									.Catch(function (e)
									{
										msg += 'catch ' + e.message;
									})
									.toArray(function(err, arr) {
										assert.equal(err, undefined);
										assert.deepEqual(arr, settings.result);
										assert.equal(msg, '');

										if (settings.enumerable.isAsynchronous) {
											callback(true);
										}
									});

								assert.deepEqual(actual, settings.enumerable.isAsynchronous ? undefined : settings.result);
								assert.equal(msg, '');

								if (!settings.enumerable.isAsynchronous) { callback(true); }
							});
						}
					},
					'#Finally(finallyAction)': {
						'finally should be called when an exception is thrown' : function(done) {
// BAD TEST: figure out how to make finally the first call off of enumerable
							runTest(done, function(callback, settings) {
								var msg    = '';
								var actual = settings.enumerable
									.select(function (v,i)
									{
										if (i == 4) throw new Error('aiueo');
										return v;
									})
									.Catch(function (e)
									{
										msg += e.message;
									})
									.Finally(function (f)
									{
										msg += 'f';
									})	
									.toArray(function(err, arr) {
										assert.deepEqual(arr, settings.result.slice(0, 4));
										assert.equal(msg, (settings.result.length <= 4) ? 'f' : 'aiueof');

										if (settings.enumerable.isAsynchronous) {
											callback(true);
										}
									});

								assert.deepEqual(actual, settings.enumerable.isAsynchronous ? undefined : settings.result.slice(0, 4));

								if (settings.enumerable.isAsynchronous) {
									assert.equal(msg, '');
								} else {
									assert.equal(msg, (settings.result.length <= 4) ? 'f' : 'aiueof');						
								}

								if (!settings.enumerable.isAsynchronous) { callback(true); }
							});
						},
						'finally should be called when an exception is not thrown' : function(done) {
							runTest(done, function(callback, settings) {
								var msg    = '';
								var actual = settings.enumerable
									.Finally(function (f)
									{
										msg += 'f';
									})	
									.toArray(function(err, arr) {
										assert.equal(err, undefined);
										assert.deepEqual(arr, settings.result);
										assert.equal(msg, 'f');

										if (settings.enumerable.isAsynchronous) {
											callback(true);
										}
									});

								assert.deepEqual(actual, settings.enumerable.isAsynchronous ? undefined : settings.result);
								assert.equal(msg, settings.enumerable.isAsynchronous ? '' : 'f');

								if (!settings.enumerable.isAsynchronous) { callback(true); }
							});
						}
					}
				},
				'Paging': {
					'#elementAt(index, [doneCallback<err, result>])': {
						'selecting the first element should return it' : function(done) {
							runTest(done, function(callback, settings) {
								if (settings.result.length < 1) {
									done();
									return;
								}

								var actual = settings.enumerable.elementAt(0, function(err, value) {
									assert.equal(err, undefined);
									assert.equal(value, settings.result[0]);
													
									if (settings.enumerable.isAsynchronous) {
										callback(true);
									}
								});

								assert.equal(actual, settings.enumerable.isAsynchronous ? undefined : settings.result[0]);

								if (!settings.enumerable.isAsynchronous) {
									actual = settings.enumerable.elementAt(0);

									assert.equal(actual, settings.result[0]);
									callback(true);
								}
							});
						},
						'selecting the last element should return it' : function(done) {
							runTest(done, function(callback, settings) {
								if (settings.result.length < 1) {
									done();
									return;
								}

								var actual = settings.enumerable.elementAt(settings.result.length - 1, function(err, value) {
									assert.equal(err, undefined);
									assert.equal(value, settings.result[settings.result.length - 1]);
													
									if (settings.enumerable.isAsynchronous) {
										callback(true);
									}
								});

								assert.equal(actual, settings.enumerable.isAsynchronous ? undefined : settings.result[settings.result.length - 1]);

								if (!settings.enumerable.isAsynchronous) {
									actual = settings.enumerable.elementAt(settings.result.length - 1);

									assert.equal(actual, settings.result[settings.result.length - 1]);
									callback(true);
								}
							});
						},
						'selecting the middle element should return it' : function(done) {
							runTest(done, function(callback, settings) {
								if (settings.result.length < 1) {
									done();
									return;
								}

								var index  = Math.floor(settings.result.length / 2);
								var actual = settings.enumerable.elementAt(index, function(err, value) {
									assert.equal(err, undefined);
									assert.equal(value, settings.result[index]);
													
									if (settings.enumerable.isAsynchronous) {
										callback(true);
									}
								});

								assert.equal(actual, settings.enumerable.isAsynchronous ? undefined : settings.result[index]);

								if (!settings.enumerable.isAsynchronous) {
									actual = settings.enumerable.elementAt(index);
											
									assert.equal(actual, settings.result[index]);
									callback(true);
								}
							});
						},
						'selecting an element outside the number of elements should return the exception in the callback' : function(done) {
							runTest(done, function(callback, settings) {
								var actual = settings.enumerable.elementAt(settings.result.length, function(err, value) {
									assert.ok(utility.isError(err));
									assert.equal(value, undefined);
											
									if (settings.enumerable.isAsynchronous) {
										callback(true);
									}
								});

								assert.equal(actual, undefined);

								if (!settings.enumerable.isAsynchronous) { callback(true); }
							});
						},
						'selecting an element outside the number of elements should return the exception in the callback (negitive)' : function(done) {
							runTest(done, function(callback, settings) {
								var actual = settings.enumerable.elementAt(-1, function(err, value) {
									assert.ok(utility.isError(err));
									assert.equal(value, undefined);
											
									if (settings.enumerable.isAsynchronous) {
										callback(true);
									}
								});

								assert.equal(actual, undefined);

								if (!settings.enumerable.isAsynchronous) { callback(true); }
							});
						},
						'selecting an element outside the number of elements should throw an exception if a callback is not supplied' : function(done) {
							runTest(done, function(callback, settings) {
								if (settings.enumerable.isAsynchronous) {
									callback(false);
									return;
								}

								assert.throws(function() { settings.enumerable.elementAt(settings.result.length) });
								callback(true);
							});
						},
						'selecting an element outside the number of elements should throw an exception if a callback is not supplied (negitive)' : function(done) {
							runTest(done, function(callback, settings) {
								if (settings.enumerable.isAsynchronous) {
									callback(false);
									return;
								}

								assert.throws(function() { settings.enumerable.elementAt(-1) });
								callback(true);
							});
						}
					},
					'#elementAtOrDefault(index, defaultValue, [doneCallback<err, result>])': {
						'selecting the first element should return it' : function(done) {
							runTest(done, function(callback, settings) {
								if (settings.result.length < 1) {
									callback(false);
									return;
								}

								var actual = settings.enumerable.elementAtOrDefault(0, 'foo', function(err, value) {
									assert.equal(err, undefined);
									assert.equal(value, settings.result[0]);
													
									if (settings.enumerable.isAsynchronous) {
										callback(true);
									}
								});

								assert.equal(actual, settings.enumerable.isAsynchronous ? undefined : settings.result[0]);

								if (!settings.enumerable.isAsynchronous) {
									actual = settings.enumerable.elementAtOrDefault(0, 'foo');

									assert.equal(actual, settings.result[0]);
									callback(true);
								}
							});
						},
						'selecting the last element should return it' : function(done) {
							runTest(done, function(callback, settings) {
								if (settings.result.length < 1) {
									callback(false);
									return;
								}

								var actual = settings.enumerable.elementAtOrDefault(settings.result.length - 1, 'foo', function(err, value) {
									assert.equal(err, undefined);
									assert.equal(value, settings.result[settings.result.length - 1]);
													
									if (settings.enumerable.isAsynchronous) {
										callback(true);
									}
								});

								assert.equal(actual, settings.enumerable.isAsynchronous ? undefined : settings.result[settings.result.length - 1]);

								if (!settings.enumerable.isAsynchronous) {
									actual = settings.enumerable.elementAtOrDefault(settings.result.length - 1, 'foo');
											
									assert.equal(actual, settings.result[settings.result.length - 1]);
									callback(true);
								}
							});
						},
						'selecting the middle element should return it' : function(done) {
							runTest(done, function(callback, settings) {
								if (settings.result.length < 1) {
									callback(false);
									return;
								}

								var index  = Math.floor(settings.result.length / 2);
								var actual = settings.enumerable.elementAtOrDefault(index, 'foo', function(err, value) {
									assert.equal(err, undefined);
									assert.equal(value, settings.result[index]);
													
									if (settings.enumerable.isAsynchronous) {
										callback(true);
									}
								});

								assert.equal(actual, settings.enumerable.isAsynchronous ? undefined : settings.result[index]);

								if (!settings.enumerable.isAsynchronous) {
									actual = settings.enumerable.elementAtOrDefault(index, 'foo');
											
									assert.equal(actual, settings.result[index]);
									callback(true);
								}
							});
						},
						'selecting an element outside the number of elements should return the default value' : function(done) {
							runTest(done, function(callback, settings) {
								var actual = settings.enumerable.elementAtOrDefault(settings.result.length, 'foo', function(err, value) {
									assert.equal(err, undefined);
									assert.equal(value, 'foo');
											
									if (settings.enumerable.isAsynchronous) {
										callback(true);
									}
								});

								assert.equal(actual, settings.enumerable.isAsynchronous ? undefined : 'foo');

								if (!settings.enumerable.isAsynchronous) {
									actual = settings.enumerable.elementAtOrDefault(settings.result.length, 'foo');
							
									assert.equal(actual, 'foo');
									callback(true);
								}
							});
						},
						'selecting an element outside the number of elements should return the default value (negitive)' : function(done) {
							runTest(done, function(callback, settings) {
								var actual = settings.enumerable.elementAtOrDefault(-1, 'foo', function(err, value) {
									assert.equal(err, undefined);
									assert.equal(value, 'foo');
											
									if (settings.enumerable.isAsynchronous) {
										callback(true);
									}
								});

								assert.equal(actual, settings.enumerable.isAsynchronous ? undefined : 'foo');

								if (!settings.enumerable.isAsynchronous) {
									actual = settings.enumerable.elementAtOrDefault(-1, 'foo');

									assert.equal(actual, 'foo');
									callback(true);
								}
							});
						}
					},
					'#first([predicate<element,index>], [doneCallback<err, result>])': {
						'an empty enumerable should error' : function(done) {
							runTest(done, function(callback, settings) {
								if (settings.result.length > 0) {
									callback(false);
									return;
								}

								var actual = settings.enumerable.first(undefined, function(err, value) {
									assert.ok(utility.isError(err));
									assert.equal(value, undefined);
											
									if (settings.enumerable.isAsynchronous) {
										callback(true);
									}
								});
									
								assert.equal(actual, undefined);

								if (!settings.enumerable.isAsynchronous) {
									assert.throws(function() { settings.enumerable.first(); });

									callback(true);
								}
							});
						},
						'calling first with a predicate that always returns false should error' : function(done) {
							runTest(done, function(callback, settings) {
								var actual = settings.enumerable.first('i=>1==2', function(err, value) {
									assert.ok(utility.isError(err));
									assert.equal(value, undefined);
											
									if (settings.enumerable.isAsynchronous) {
										callback(true);
									}
								});
									
								assert.equal(actual, undefined);

								if (!settings.enumerable.isAsynchronous) {
									assert.throws(function() { settings.enumerable.first('i=>1==2'); });

									callback(true);
								}
							});
						},
						'a non-empty enumerable should return the first value' : function(done) {
							runTest(done, function(callback, settings) {
								if (settings.result.length < 1) {
									callback(false);
									return;
								}

								var actual = settings.enumerable.first(undefined, function(err, value) {
									assert.equal(err, undefined);
									assert.equal(value, settings.result[0]);
											
									if (settings.enumerable.isAsynchronous) {
										callback(true);
									}
								});

								assert.equal(actual, settings.enumerable.isAsynchronous ? undefined : settings.result[0]);

								if (!settings.enumerable.isAsynchronous) {
									actual = settings.enumerable.first();
											
									assert.equal(actual, settings.result[0]);
									callback(true);
								}
							});
						},
						'calling first with a predicate should return the first item that matches the predicate' : function(done) {
							runTest(done, function(callback, settings) {
								if (settings.result.length < 3) {
									callback(false);
									return;
								}

								var actual = settings.enumerable.first('v,i=>i>0', function(err, value) {
									assert.equal(err, undefined);
									assert.equal(value, settings.result[1]);
											
									if (settings.enumerable.isAsynchronous) {
										callback(true);
									}
								});

								assert.equal(actual, settings.enumerable.isAsynchronous ? undefined : settings.result[1]);

								if (!settings.enumerable.isAsynchronous) {
									actual = settings.enumerable.first('v,i=>i>0');
											
									assert.equal(actual, settings.result[1]);
									callback(true);
								}
							});
						}
					},
					'#firstOrDefault(defaultValue, [predicate<element,index>], [doneCallback<err, result>])' : {
						'an empty enumerable should return the default value': function(done) {
							runTest(done, function(callback, settings) {
								if (settings.result.length > 0) {
									callback(false);
									return;
								}

								var actual = settings.enumerable.firstOrDefault(4, undefined, function(err, value) {
									assert.equal(err, undefined);
									assert.equal(value, 4);
													
									if (settings.enumerable.isAsynchronous) {
										callback(true);
									}
								});

								assert.equal(actual, !settings.enumerable.isAsynchronous ? 4 : undefined);

								if (!settings.enumerable.isAsynchronous) {
									actual = settings.enumerable.firstOrDefault(4);

									assert.equal(actual, 4);
									callback(true);
								}
							});
						},
						'a non-empty enumerable should return the first value': function(done) {
							runTest(done, function(callback, settings) {
								if (settings.result.length < 1) {
									callback(false);
									return;
								}

								var actual = settings.enumerable.firstOrDefault(4, undefined, function(err, value) {
									assert.equal(err, undefined);
									assert.equal(value, settings.result[0]);

									if (settings.enumerable.isAsynchronous) {
										callback(true);
									}
								});

								assert.equal(actual, !settings.enumerable.isAsynchronous ? settings.result[0] : undefined);

								if (!settings.enumerable.isAsynchronous) {
									actual = settings.enumerable.firstOrDefault(4);

									assert.equal(actual, settings.result[0]);
									callback(true);
								}
							});
						},
						'a predicate should pass the current item as the first argument and the index as the second': function(done) {
							runTest(done, function(callback, settings) {
								var index  = -1;
								var actual = settings.enumerable.firstOrDefault(4, function(i,j) { index++; return !(i === settings.result[index] && j === index); }, function(err, value) {
									assert.equal(err, undefined);
									assert.equal(value, 4);
													
									if (settings.enumerable.isAsynchronous) {
										callback(true);
									}
								});

								assert.equal(actual, !settings.enumerable.isAsynchronous ? 4 : undefined);

								if (!settings.enumerable.isAsynchronous) {
									index  = -1;
									actual = settings.enumerable.firstOrDefault(4, function(i,j) { index++;  return !(i === settings.result[index] && j === index); });

									assert.equal(actual, 4);
									callback(true);
								}
							});
						},
						'a predicate that returns false for all values should return the default value': function(done) {
							runTest(done, function(callback, settings) {
								var actual = settings.enumerable.firstOrDefault(4, 'i=>false', function(err, value) {
									assert.equal(err, undefined);
									assert.equal(value, 4);
													
									if (settings.enumerable.isAsynchronous) {
										callback(true);
									}
								});

								assert.equal(actual, !settings.enumerable.isAsynchronous ? 4 : undefined);

								if (!settings.enumerable.isAsynchronous) {
									actual = settings.enumerable.firstOrDefault(4, 'i=>false');

									assert.equal(actual, 4);
									callback(true);
								}
							});
						},
						'a predicate that returns true for a value should return that value': function(done) {
							runTest(done, function(callback, settings) {
								if (settings.result.length < 1) {
									callback(false);
									return;
								}
								var actual = settings.enumerable.firstOrDefault(4, 'i=>true', function(err, value) {
									assert.equal(err, undefined);
									assert.equal(value, settings.result[0]);
													
									if (settings.enumerable.isAsynchronous) {
										callback(true);
									}
								});

								assert.equal(actual, !settings.enumerable.isAsynchronous ? settings.result[0] : undefined);

								if (!settings.enumerable.isAsynchronous) {
									actual = settings.enumerable.firstOrDefault(settings.result[0], 'i=>true');

									assert.equal(actual, settings.result[0]);
									callback(true);
								}
							});
						}
					},
					'#last([predicate<element,index>], [doneCallback<err, result>])': {
						'an empty enumerable should error' : function(done) {
							runTest(done, function(callback, settings) {
								if (settings.result.length > 0) {
									callback(false);
									return;
								}

								var actual = settings.enumerable.skip(settings.result.length).last(undefined, function(err, value) {
									assert.ok(utility.isError(err));
									assert.equal(value, undefined);
											
									if (settings.enumerable.isAsynchronous) {
										callback(true);
									}
								});
									
								assert.equal(actual, undefined);

								if (!settings.enumerable.isAsynchronous) {
									assert.throws(function() { settings.enumerable.last(); });
									callback(true);
								}
							});
						},
						'a predicate that always returns false should error' : function(done) {
							runTest(done, function(callback, settings) {
								var actual = settings.enumerable.last('i=>1==2', function(err, value) {
									assert.ok(utility.isError(err));
									assert.equal(value, undefined);
											
									if (settings.enumerable.isAsynchronous) {
										callback(true);
									}
								});
									
								assert.equal(actual, undefined);

								if (!settings.enumerable.isAsynchronous) {
									assert.throws(function() { settings.enumerable.last('i=>1==2'); });
									callback(true);
								}
							});
						},
						'a non-empty enumerable should return the last value' : function(done) {
							runTest(done, function(callback, settings) {
								if (settings.result.length < 1) {
									callback(false);
									return;
								}

								if (settings.result.length < 1) {
									callback(false);
									return;
								}

								var actual = settings.enumerable.last(undefined, function(err, value) {
									assert.equal(err, undefined);
									assert.deepEqual(value, settings.result[settings.result.length -1]);
											
									if (settings.enumerable.isAsynchronous) {
										callback(true);
									}
								});

								assert.deepEqual(actual, settings.enumerable.isAsynchronous ? undefined : settings.result[settings.result.length -1]);

								if (!settings.enumerable.isAsynchronous) {
									actual = settings.enumerable.last();
											
									assert.deepEqual(actual, settings.result[settings.result.length -1]);
									callback(true);
								}
							});
						},
						'a predicate should return the last item that matches the predicate' : function(done) {
							runTest(done, function(callback, settings) {
								if (settings.result.length < 3) {
									callback(false);
									return;
								}

								var actual = settings.enumerable.last('v,i=>i<2', function(err, value) {
									assert.equal(err, undefined);
									assert.equal(value, settings.result[1]);
											
									if (settings.enumerable.isAsynchronous) {
										callback(true);
									}
								});

								assert.equal(actual, settings.enumerable.isAsynchronous ? undefined : settings.result[1]);

								if (!settings.enumerable.isAsynchronous) {
									actual = settings.enumerable.last('v,i=>i<2');
											
									assert.equal(actual, settings.result[1]);
									callback(true);
								}
							});
						}
					},
					'#lastOrDefault(defaultValue, [predicate<element,index>], [doneCallback<err, result>])': {
						'an empty enumerable should return the default value': function(done) {
							runTest(done, function(callback, settings) {
								if (settings.result.length > 0) {
									callback(false);
									return;
								}

								var actual = settings.enumerable.lastOrDefault(4, undefined, function(err, value) {
									assert.equal(err, undefined);
									assert.equal(value, 4);
													
									if (settings.enumerable.isAsynchronous) {
										callback(true);
									}
								});

								assert.equal(actual, !settings.enumerable.isAsynchronous ? 4 : undefined);

								if (!settings.enumerable.isAsynchronous) {
									actual = settings.enumerable.lastOrDefault(4);

									assert.equal(actual, 4);
									callback(true);
								}
							});
						},
						'a non-empty enumerable should return the last value': function(done) {
							runTest(done, function(callback, settings) {
								if (settings.result.length < 1) {
									callback(false);
									return;
								}

								var actual = settings.enumerable.lastOrDefault(4, undefined, function(err, value) {
									assert.equal(err, undefined);
									assert.deepEqual(value, settings.result[settings.result.length - 1]);

									if (settings.enumerable.isAsynchronous) {
										callback(true);
									}
								});

								assert.deepEqual(actual, !settings.enumerable.isAsynchronous ? settings.result[settings.result.length - 1] : undefined);

								if (!settings.enumerable.isAsynchronous) {
									actual = settings.enumerable.lastOrDefault(4);

									assert.deepEqual(actual, settings.result[settings.result.length - 1]);
									callback(true);
								}
							});
						},
						'a predicate should pass the current item as the last argument and the index as the second': function(done) {
							runTest(done, function(callback, settings) {
								var index  = -1;
								var actual = settings.enumerable.lastOrDefault(4, function(i,j) { index++; return !(i === settings.result[index] && j === index); }, function(err, value) {
									assert.equal(err, undefined);
									assert.equal(value, 4);
													
									if (settings.enumerable.isAsynchronous) {
										callback(true);
									}
								});

								assert.equal(actual, !settings.enumerable.isAsynchronous ? 4 : undefined);

								if (!settings.enumerable.isAsynchronous) {
									index  = -1;
									actual = settings.enumerable.lastOrDefault(4, function(i,j) { index++; return !(i === settings.result[index] && j === index); });

									assert.equal(actual, 4);
									callback(true);
								}
							});
						},
						'a predicate that returns false for all values should return the default value': function(done) {
							runTest(done, function(callback, settings) {
								var actual = settings.enumerable.lastOrDefault(4, 'i=>false', function(err, value) {
									assert.equal(err, undefined);
									assert.equal(value, 4);
													
									if (settings.enumerable.isAsynchronous) {
										callback(true);
									}
								});

								assert.equal(actual, !settings.enumerable.isAsynchronous ? 4 : undefined);

								if (!settings.enumerable.isAsynchronous) {
									actual = settings.enumerable.lastOrDefault(4, 'i=>false');

									assert.equal(actual, 4);
									callback(true);
								}
							});
						},
						'a predicate that returns true should return that value': function(done) {
							runTest(done, function(callback, settings) {
								if (settings.result.length < 1) {
									callback(false);
									return;
								}
								var actual = settings.enumerable.lastOrDefault(4, 'i=>true', function(err, value) {
									assert.equal(err, undefined);
									assert.deepEqual(value, settings.result[settings.result.length - 1]);
													
									if (settings.enumerable.isAsynchronous) {
										callback(true);
									}
								});

								assert.deepEqual(actual, !settings.enumerable.isAsynchronous ? settings.result[settings.result.length - 1] : undefined);

								if (!settings.enumerable.isAsynchronous) {
									actual = settings.enumerable.lastOrDefault(settings.result[settings.result.length - 1], 'i=>true');

									assert.deepEqual(actual, settings.result[settings.result.length - 1]);
									callback(true);
								}
							});
						}
					},
					'#single([predicate<element,index>], [doneCallback<err, result>])': {
						'an empty enumerable should error' : function(done) {
							runTest(done, function(callback, settings) {
								if (settings.result.length > 0) {
									callback(false);
									return;
								}

								var actual = settings.enumerable.single(undefined, function(err, value) {
									assert.ok(utility.isError(err));
									assert.equal(value, undefined);
											
									if (settings.enumerable.isAsynchronous) {
										callback(true);
									}
								});
									
								assert.equal(actual, undefined);

								if (!settings.enumerable.isAsynchronous) {
									assert.throws(function() { settings.enumerable.single(); });

									callback(true);
								}
							});
						},
						'a predicate that always returns false should error' : function(done) {
							runTest(done, function(callback, settings) {
								var actual = settings.enumerable.single('i=>1==2', function(err, value) {
									assert.ok(utility.isError(err));
									assert.equal(value, undefined);
											
									if (settings.enumerable.isAsynchronous) {
										callback(true);
									}
								});
									
								assert.equal(actual, undefined);

								if (!settings.enumerable.isAsynchronous) {
									assert.throws(function() { settings.enumerable.single('i=>1==2'); });
									callback(true);
								}
							});
						},
						'a predicate that always returns true should return the exception' : function(done) {
							runTest(done, function(callback, settings) {
								if (settings.result.length < 2) {
									callback(false);
									return;
								}

								var actual = settings.enumerable.single('i=>true', function(err, value) {
									assert.ok(utility.isError(err));
									assert.equal(value, undefined);
											
									if (settings.enumerable.isAsynchronous) {
										callback(true);
									}
								});
									
								assert.equal(actual, undefined);

								if (!settings.enumerable.isAsynchronous) {
									assert.throws(function() { settings.enumerable.single('i=>true') });

									callback(true);
								}
							});
						},
						'a non-empty enumerable should return the first value' : function(done) {
							runTest(done, function(callback, settings) {
								if (settings.result.length < 1) {
									callback(false);
									return;
								}

								var actual = settings.enumerable.take(1).single(undefined, function(err, value) {
									assert.equal(err, undefined);
									assert.equal(value, settings.result[0]);
											
									if (settings.enumerable.isAsynchronous) {
										callback(true);
									}
								});

								assert.equal(actual, settings.enumerable.isAsynchronous ? undefined : settings.result[0]);

								if (!settings.enumerable.isAsynchronous) {
									actual = settings.enumerable.take(1).single();
											
									assert.equal(actual, settings.result[0]);
									callback(true);
								}
							});
						},
						'a predicate should return the item that matches the predicate' : function(done) {
							runTest(done, function(callback, settings) {
								if (settings.result.length < 3) {
									callback(false);
									return;
								}

								var actual = settings.enumerable.single('v,i=>i==1', function(err, value) {
									assert.equal(err, undefined);
									assert.equal(value, settings.result[1]);
											
									if (settings.enumerable.isAsynchronous) {
										callback(true);
									}
								});

								assert.equal(actual, settings.enumerable.isAsynchronous ? undefined : settings.result[1]);

								if (!settings.enumerable.isAsynchronous) {
									actual = settings.enumerable.single('v,i=>i==1');
											
									assert.equal(actual, settings.result[1]);
									callback(true);
								}
							});
						}
					},
					'#singleOrDefault(defaultValue, [predicate<element,index>], [doneCallback<err, result>])': {
						'an empty enumerable should return the default value': function(done) {
							runTest(done, function(callback, settings) {
								if (settings.result.length > 0) {
									callback(false);
									return;
								}

								var actual = settings.enumerable.singleOrDefault(4, undefined, function(err, value) {
									assert.equal(err, undefined);
									assert.equal(value, 4);
													
									if (settings.enumerable.isAsynchronous) {
										callback(true);
									}
								});

								assert.equal(actual, !settings.enumerable.isAsynchronous ? 4 : undefined);

								if (!settings.enumerable.isAsynchronous) {
									actual = settings.enumerable.singleOrDefault(4);

									assert.equal(actual, 4);
									callback(true);
								}
							});
						},
						'a enumerable with one element should return that element': function(done) {
							runTest(done, function(callback, settings) {
								if (settings.result.length !== 1) {
									callback(false);
									return;
								}

								var actual = settings.enumerable.singleOrDefault(4, undefined, function(err, value) {
									assert.equal(err, undefined);
									assert.equal(value, settings.result[0]);

									if (settings.enumerable.isAsynchronous) {
										callback(true);
									}
								});

								assert.equal(actual, !settings.enumerable.isAsynchronous ? settings.result[0] : undefined);

								if (!settings.enumerable.isAsynchronous) {
									actual = settings.enumerable.singleOrDefault(4);

									assert.equal(actual, settings.result[0]);
									callback(true);
								}
							});
						},
						'a predicate should pass the current item as the first argument and the index as the second': function(done) {
							runTest(done, function(callback, settings) {
								var index  = -1;
								var actual = settings.enumerable.singleOrDefault(4, function(i,j) { index++; return !(i === settings.result[index] && j === index); }, function(err, value) {
									assert.equal(err, undefined);
									assert.equal(value, 4);
													
									if (settings.enumerable.isAsynchronous) {
										callback(true);
									}
								});

								assert.equal(actual, !settings.enumerable.isAsynchronous ? 4 : undefined);

								if (!settings.enumerable.isAsynchronous) {
									index  = -1;
									actual = settings.enumerable.singleOrDefault(4, function(i,j) { index++; return !(i === settings.result[index] && j === index); });

									assert.equal(actual, 4);
									callback(true);
								}
							});
						},
						'a predicate that returns false for all values should return the default value': function(done) {
							runTest(done, function(callback, settings) {
								var actual = settings.enumerable.singleOrDefault(4, 'i=>false', function(err, value) {
									assert.equal(err, undefined);
									assert.equal(value, 4);
													
									if (settings.enumerable.isAsynchronous) {
										callback(true);
									}
								});

								assert.equal(actual, !settings.enumerable.isAsynchronous ? 4 : undefined);

								if (!settings.enumerable.isAsynchronous) {
									actual = settings.enumerable.singleOrDefault(4, 'i=>false');

									assert.equal(actual, 4);
									callback(true);
								}
							});
						},
						'a predicate that returns true for all values in an enumerable with more than one item should error': function(done) {
							runTest(done, function(callback, settings) {
								if (settings.result.length < 2) {
									callback(false);
									return;
								}
								var actual = settings.enumerable.singleOrDefault(4, 'i=>true', function(err, value) {
									assert.ok(utility.isError(err));
									assert.equal(value, undefined);
													
									if (settings.enumerable.isAsynchronous) {
										callback(true);
									}
								});

								assert.equal(actual, undefined);

								if (!settings.enumerable.isAsynchronous) {
									assert.throws(function() { settings.enumerable.singleOrDefault(4, 'i=>true') });
									callback(true);
								}
							});
						}
					},
					'#skip(count)': {
						'skipping the first element shoud return all other items' : function(done) {
							runTest(done, function(callback, settings) {
								if (settings.result.length < 1) {
									callback(false);
									return;
								}

								var actual = settings.enumerable.skip(1).toArray(function(err, arr) {
									assert.equal(err, undefined);
									assert.deepEqual(arr, settings.result.slice(1));

									if (settings.enumerable.isAsynchronous) {
										callback(true);
									}
								});

								assert.deepEqual(actual, settings.enumerable.isAsynchronous ? undefined : settings.result.slice(1));

								if (!settings.enumerable.isAsynchronous) {
									actual = settings.enumerable.skip(1).toArray();

									assert.deepEqual(actual, settings.result.slice(1));
									callback(true);
								}
							});
						},
						'skipping all but the last should return only the last item' : function(done) {
							runTest(done, function(callback, settings) {
								if (settings.result.length < 1) {
									callback(false);
									return;
								}

								var actual = settings.enumerable.skip(settings.result.length - 1).toArray(function(err, arr) {
									assert.equal(err, undefined);
									assert.deepEqual(arr, settings.result.slice(settings.result.length - 1));

									if (settings.enumerable.isAsynchronous) {
										callback(true);
									}
								});

								assert.deepEqual(actual, settings.enumerable.isAsynchronous ? undefined : settings.result.slice(settings.result.length - 1));

								if (!settings.enumerable.isAsynchronous) {
									actual = settings.enumerable.skip(settings.result.length - 1).toArray();

									assert.deepEqual(actual, settings.result.slice(settings.result.length - 1));
									callback(true);
								}
							});
						},
						'skipping all items shouldnt return anything' : function(done) {
							runTest(done, function(callback, settings) {
								var actual = settings.enumerable.skip(settings.result.length).toArray(function(err, arr) {
									assert.equal(err, undefined);
									assert.deepEqual(arr, []);

									if (settings.enumerable.isAsynchronous) {
										callback(true);
									}
								});

								assert.deepEqual(actual, settings.enumerable.isAsynchronous ? undefined : []);

								if (!settings.enumerable.isAsynchronous) {
									actual = settings.enumerable.skip(settings.result.length).toArray();

									assert.deepEqual(actual, []);
									callback(true);
								}
							});

						}
					},
					'#skipWhile(predicate<element,index>)': {
						'a predicate should pass the current item as the first argument and the index as the second': function(done) {
							runTest(done, function(callback, settings) {
								var index  = 0;
								var actual = settings.enumerable.skipWhile(function(v,i) { return !(v === settings.result[index++] && i === index); }).toArray(function(err, arr) {
									assert.equal(err, undefined);
									assert.deepEqual(arr, []);
													
									if (settings.enumerable.isAsynchronous) {
										callback(true);
									}
								});

								assert.deepEqual(actual, !settings.enumerable.isAsynchronous ? [] : undefined);

								if (!settings.enumerable.isAsynchronous) {
									index  = 0;
									actual = settings.enumerable.skipWhile(function(v,i) { return !(v === settings.result[index++] && i === index); }).toArray();

									assert.deepEqual(actual, []);
									callback(true);
								}
							});
						},
						'if no predicate is supplied it shouldnt return any elements' : function(done) {
							runTest(done, function(callback, settings) {
								var actual = settings.enumerable.skipWhile().toArray(function(err, arr) {
									assert.equal(err, undefined);
									assert.deepEqual(arr, []);
													
									if (settings.enumerable.isAsynchronous) {
										callback(true);
									}
								});

								assert.deepEqual(actual, !settings.enumerable.isAsynchronous ? [] : undefined);

								if (!settings.enumerable.isAsynchronous) {
									actual = settings.enumerable.skipWhile().toArray();

									assert.deepEqual(actual, []);
									callback(true);
								}
							});
						},
						'it should skip items where the predicate returns true' : function(done) {
							runTest(done, function(callback, settings) {
								var actual = settings.enumerable.skipWhile('v,i=>i<5').toArray(function(err, arr) {
									assert.equal(err, undefined);
									assert.deepEqual(arr, settings.result.slice(5));
											
									if (settings.enumerable.isAsynchronous) {
										callback(true);
									}
								});

								assert.deepEqual(actual, !settings.enumerable.isAsynchronous ? settings.result.slice(5) : undefined);

								if (!settings.enumerable.isAsynchronous) {
									actual = settings.enumerable.skipWhile('v,i=>i<5').toArray();

									assert.deepEqual(actual, settings.result.slice(5));
									callback(true);
								}
							});
						}
					},
					'#take(count)': {
						'it should return the number of items specified' : function(done) {
							runTest(done, function(callback, settings) {
								var actual = settings.enumerable.take(4).toArray(function(err, arr) {
									assert.equal(err, undefined);
									assert.deepEqual(arr, settings.result.slice(0,4));
											
									if (settings.enumerable.isAsynchronous) {
										callback(true);
									}
								});

								assert.deepEqual(actual, !settings.enumerable.isAsynchronous ? settings.result.slice(0,4) : undefined);

								if (!settings.enumerable.isAsynchronous) {
									actual = settings.enumerable.take(4).toArray();

									assert.deepEqual(actual, settings.result.slice(0,4));
									callback(true);
								}
							});
						}
					},
					'#takeWhile(predicate<element,index>)': {
						'a predicate should pass the current item as the first argument and the index as the second': function(done) {
							runTest(done, function(callback, settings) {
								var index  = 0;
								var actual = settings.enumerable.takeWhile(function(v,i) { return (v === settings.result[index++] && i === index); }).toArray(function(err, arr) {
									assert.equal(err, undefined);
									assert.deepEqual(arr, []);
													
									if (settings.enumerable.isAsynchronous) {
										callback(true);
									}
								});

								assert.deepEqual(actual, !settings.enumerable.isAsynchronous ? [] : undefined);

								if (!settings.enumerable.isAsynchronous) {
									index  = 0;
									actual = settings.enumerable.takeWhile(function(v,i) { return (v === settings.result[index++] && i === index); }).toArray();

									assert.deepEqual(actual, []);
									callback(true);
								}
							});
						},
						'if no predicate is supplied it should return all elements' : function(done) {
							runTest(done, function(callback, settings) {
								var actual = settings.enumerable.takeWhile().toArray(function(err, arr) {
									assert.equal(err, undefined);
									assert.deepEqual(arr, settings.result);
													
									if (settings.enumerable.isAsynchronous) {
										callback(true);
									}
								});

								assert.deepEqual(actual, !settings.enumerable.isAsynchronous ? settings.result : undefined);

								if (!settings.enumerable.isAsynchronous) {
									actual = settings.enumerable.takeWhile().toArray();

									assert.deepEqual(actual, settings.result);
									callback(true);
								}
							});
						},
						'items where a predicate returns false should be excluded' : function(done) {
							runTest(done, function(callback, settings) {
								var actual = settings.enumerable.takeWhile('v,i=>i<5').toArray(function(err, arr) {
									assert.equal(err, undefined);
									assert.deepEqual(arr, settings.result.slice(0,5));
													
									if (settings.enumerable.isAsynchronous) {
										callback(true);
									}
								});

								assert.deepEqual(actual, !settings.enumerable.isAsynchronous ? settings.result.slice(0,5) : undefined);

								if (!settings.enumerable.isAsynchronous) {
									actual = settings.enumerable.takeWhile('v,i=>i<5').toArray();

									assert.deepEqual(actual, settings.result.slice(0,5));
									callback(true);
								}
							});
						}
					},
					'#takeExceptLast([count])': {
						'if no count is specified it should return all but the last element' : function(done) {
							runTest(done, function(callback, settings) {
								var actual = settings.enumerable.takeExceptLast().toArray(function(err, arr) {
									assert.equal(err, undefined);
									assert.deepEqual(arr, settings.result.slice(0, settings.result.length - 1));
													
									if (settings.enumerable.isAsynchronous) {
										callback(true);
									}
								});

								assert.deepEqual(actual, !settings.enumerable.isAsynchronous ? settings.result.slice(0, settings.result.length - 1) : undefined);

								if (!settings.enumerable.isAsynchronous) {
									actual = settings.enumerable.takeExceptLast().toArray();

									assert.deepEqual(actual, settings.result.slice(0, settings.result.length - 1));
									callback(true);
								}
							});
						},
						'it should return all but the last n elements' : function(done) {
							runTest(done, function(callback, settings) {
								var actual = settings.enumerable.takeExceptLast(3).toArray(function(err, arr) {
									assert.equal(err, undefined);
									assert.deepEqual(arr, settings.result.slice(0, settings.result.length - 3));
													
									if (settings.enumerable.isAsynchronous) {
										callback(true);
									}
								});

								assert.deepEqual(actual, !settings.enumerable.isAsynchronous ? settings.result.slice(0, settings.result.length - 3) : undefined);

								if (!settings.enumerable.isAsynchronous) {
									actual = settings.enumerable.takeExceptLast(3).toArray();

									assert.deepEqual(actual, settings.result.slice(0, settings.result.length - 3));
									callback(true);
								}
							});
						},
						'if a negitive number is specified it should return all elements' : function(done) {
							runTest(done, function(callback, settings) {
								var actual = settings.enumerable.takeExceptLast(-1).toArray(function(err, arr) {
									assert.equal(err, undefined);
									assert.deepEqual(arr, settings.result);
													
									if (settings.enumerable.isAsynchronous) {
										callback(true);
									}
								});

								assert.deepEqual(actual, !settings.enumerable.isAsynchronous ? settings.result : undefined);

								if (!settings.enumerable.isAsynchronous) {
									actual = settings.enumerable.takeExceptLast(-1).toArray();

									assert.deepEqual(actual, settings.result);
									callback(true);
								}
							});
						},
						'specifying a number larger than the number of elements shouldnt return any elements' : function(done) {
							runTest(done, function(callback, settings) {
								var actual = settings.enumerable.takeExceptLast(settings.result.length + 1).toArray(function(err, arr) {
									assert.equal(err, undefined);
									assert.deepEqual(arr, []);
													
									if (settings.enumerable.isAsynchronous) {
										callback(true);
									}
								});

								assert.deepEqual(actual, !settings.enumerable.isAsynchronous ? [] : undefined);

								if (!settings.enumerable.isAsynchronous) {
									actual = settings.enumerable.takeExceptLast(settings.result.length + 1).toArray();

									assert.deepEqual(actual, []);
									callback(true);
								}
							});
						}
					},
					'#takeFromLast(count)': {
						'it should return the last n number of elements' : function(done) {
							runTest(done, function(callback, settings) {
								var actual = settings.enumerable.takeFromLast(3).toArray(function(err, arr) {
									assert.equal(err, undefined);
									assert.deepEqual(arr, settings.result.slice(settings.result.length - 3));
													
									if (settings.enumerable.isAsynchronous) {
										callback(true);
									}
								});

								assert.deepEqual(actual, !settings.enumerable.isAsynchronous ? settings.result.slice(settings.result.length - 3) : undefined);

								if (!settings.enumerable.isAsynchronous) {
									actual = settings.enumerable.takeFromLast(3).toArray();

									assert.deepEqual(actual, settings.result.slice(settings.result.length - 3));
									callback(true);
								}
							});
						},
						'specifying a number larger than the number of elements should return all elements' : function(done) {
							runTest(done, function(callback, settings) {
								var actual = settings.enumerable.takeFromLast(settings.result.length + 1).toArray(function(err, arr) {
									assert.equal(err, undefined);
									assert.deepEqual(arr, settings.result);
													
									if (settings.enumerable.isAsynchronous) {
										callback(true);
									}
								});

								assert.deepEqual(actual, !settings.enumerable.isAsynchronous ? settings.result : undefined);

								if (!settings.enumerable.isAsynchronous) {
									actual = settings.enumerable.takeFromLast(settings.result.length + 1).toArray();

									assert.deepEqual(actual, settings.result);
									callback(true);
								}
							});
						},
						'specifying 0 should return no elements' : function(done) {
							runTest(done, function(callback, settings) {
								var actual = settings.enumerable.takeFromLast(0).toArray(function(err, arr) {
									assert.equal(err, undefined);
									assert.deepEqual(arr, []);
													
									if (settings.enumerable.isAsynchronous) {
										callback(true);
									}
								});

								assert.deepEqual(actual, !settings.enumerable.isAsynchronous ? [] : undefined);

								if (!settings.enumerable.isAsynchronous) {
									actual = settings.enumerable.takeFromLast(0).toArray();

									assert.deepEqual(actual, []);
									callback(true);
								}
							});
						},
						'specifying a negitive number should return no elements' : function(done) {
							runTest(done, function(callback, settings) {
								var actual = settings.enumerable.takeFromLast(-1).toArray(function(err, arr) {
									assert.equal(err, undefined);
									assert.deepEqual(arr, []);
											
									if (settings.enumerable.isAsynchronous) {
										callback(true);
									}
								});

								assert.deepEqual(actual, !settings.enumerable.isAsynchronous ? [] : undefined);

								if (!settings.enumerable.isAsynchronous) {
									actual = settings.enumerable.takeFromLast(-1).toArray();

									assert.deepEqual(actual, []);
									callback(true);
								}
							});
						},
					},
					'#indexOf(item, [doneCallback<err, result>])': {
						'an empty enumerable should return an index of -1' : function(done) {
							runTest(done, function(callback, settings) {
								if (settings.result.length > 0) {
									callback(false);
									return;
								}

								var actual = settings.enumerable.indexOf(4, function(err, index) {
									assert.equal(err, undefined);
									assert.equal(index, -1);
													
									if (settings.enumerable.isAsynchronous) {
										callback(true);
									}
								});

								assert.equal(actual, !settings.enumerable.isAsynchronous ? -1 : undefined);

								if (!settings.enumerable.isAsynchronous) {
									actual = settings.enumerable.indexOf(4);

									assert.equal(actual, -1);
									callback(true);
								}
							});
						},
						'it should return the index of the item specified' : function(done) {
							runTest(done, function(callback, settings) {
								if (settings.result.length < 1) {
									callback(true);
									return;
								}
								var item   = settings.result.length > 4 ? settings.result[4] : settings.result[settings.result.length - 1];
								var actual = settings.enumerable.indexOf(item, function(err, index) {
									assert.equal(err, undefined);
									assert.equal(index, settings.result.indexOf(item));
													
									if (settings.enumerable.isAsynchronous) {
										callback(true);
									}
								});

								assert.equal(actual, !settings.enumerable.isAsynchronous ? settings.result.indexOf(item) : undefined);

								if (!settings.enumerable.isAsynchronous) {
									actual = settings.enumerable.indexOf(item);

									assert.equal(actual, settings.result.indexOf(item));
									callback(true);
								}
							});
						}
					},
					'#lastIndexOf(item, [doneCallback<err, result>])': {
						'an empty enumerable should return an index of -1' : function(done) {
							runTest(done, function(callback, settings) {
								if (settings.result.length > 0) {
									callback(false);
									return;
								}

								var actual = settings.enumerable.lastIndexOf(4, function(err, index) {
									assert.equal(err, undefined);
									assert.equal(index, -1);
													
									if (settings.enumerable.isAsynchronous) {
										callback(true);
									}
								});

								assert.equal(actual, !settings.enumerable.isAsynchronous ? -1 : undefined);

								if (!settings.enumerable.isAsynchronous) {
									actual = settings.enumerable.lastIndexOf(4);

									assert.equal(actual, -1);
									callback(true);
								}
							});
						},
						'it should return the index of the item specified' : function(done) {
							runTest(done, function(callback, settings) {
								if (settings.result.length < 1) {
									callback(false);
									return;
								}
								var item   = settings.result[settings.result.length - 1];
								var actual = settings.enumerable.lastIndexOf(item, function(err, index) {
									assert.equal(err, undefined);
									assert.equal(index, settings.result.lastIndexOf(item));
													
									if (settings.enumerable.isAsynchronous) {
										callback(true);
									}
								});

								assert.equal(actual, !settings.enumerable.isAsynchronous ? settings.result.lastIndexOf(item) : undefined);

								if (!settings.enumerable.isAsynchronous) {
									actual = settings.enumerable.lastIndexOf(item);

									assert.equal(actual, settings.result.lastIndexOf(item));
									callback(true);
								}
							});
						}
					}
				},
				'Set': {
					'#all(predicate<element,index>, [doneCallback<err, result>])': {
						'an empty enumerable should return true' : function(done) {
							runTest(done, function(callback, settings) {
								if (settings.result.length > 0) {
									callback(false);
									return;
								}

								var actual = settings.enumerable.all(undefined, function(err, value) {
									assert.equal(err, undefined);
									assert.equal(value, true);
													
									if (settings.enumerable.isAsynchronous) {
										callback(true);
									}
								});

								assert.equal(actual, !settings.enumerable.isAsynchronous ? true : undefined);

								if (!settings.enumerable.isAsynchronous) {
									actual = settings.enumerable.all();

									assert.equal(actual, true);
									callback(true);
								}
							});
						},
						'a predicate that returns false should cause the result to be false' : function(done) {
							runTest(done, function(callback, settings) {
								if (settings.result.length < 1) {
									callback(false);
									return;
								}

								var actual = settings.enumerable.all(function(v,i) { if (i === settings.result.length - 1) { return false; } }, function(err, value) {
									assert.equal(err, undefined);
									assert.equal(value, false);
													
									if (settings.enumerable.isAsynchronous) {
										callback(true);
									}
								});

								assert.equal(actual, !settings.enumerable.isAsynchronous ? false : undefined);

								if (!settings.enumerable.isAsynchronous) {
									actual = settings.enumerable.all(function(v,i) { if (i === settings.result.length - 1) { return false; } });

									assert.equal(actual, false);
									callback(true);
								}
							});
						},
						'a predicate that always returns true should cause the result to be true' : function(done) {
							runTest(done, function(callback, settings) {
								var actual = settings.enumerable.all(function(v,i) { return true; }, function(err, value) {
									assert.equal(err, undefined);
									assert.equal(value, true);
													
									if (settings.enumerable.isAsynchronous) {
										callback(true);
									}
								});

								assert.equal(actual, !settings.enumerable.isAsynchronous ? true : undefined);

								if (!settings.enumerable.isAsynchronous) {
									actual = settings.enumerable.all(function(v,i) { return true; });

									assert.equal(actual, true);
									callback(true);
								}
							});
						},
						'if no predicate is specified it should return true' : function(done) {
							runTest(done, function(callback, settings) {
								var actual = settings.enumerable.all(undefined, function(err, value) {
									assert.equal(err, undefined);
									assert.equal(value, true);
													
									if (settings.enumerable.isAsynchronous) {
										callback(true);
									}
								});

								assert.equal(actual, !settings.enumerable.isAsynchronous ? true : undefined);

								if (!settings.enumerable.isAsynchronous) {
									actual = settings.enumerable.all();

									assert.equal(actual, true);
									callback(true);
								}
							});
						},
						'a predicate should pass the current item as the last argument and the index as the second': function(done) {
							runTest(done, function(callback, settings) {
								var index  = -1;
								var actual = settings.enumerable.all(function(i,j) { index++; return (i === settings.result[index] && j === index); }, function(err, value) {
									assert.equal(err, undefined);
									assert.equal(value, true);
													
									if (settings.enumerable.isAsynchronous) {
										callback(true);
									}
								});

								assert.equal(actual, !settings.enumerable.isAsynchronous ? true : undefined);

								if (!settings.enumerable.isAsynchronous) {
									index  = -1;
									actual = settings.enumerable.all(function(i,j) { index++; return (i === settings.result[index] && j === index); });

									assert.equal(actual, true);
									callback(true);
								}
							});
						},
					},
					'#any([predicate<element,index>], [doneCallback<err, result>])': {
						'an empty enumerable should return false' : function(done) {
							runTest(done, function(callback, settings) {
								if (settings.result.length > 0) {
									callback(false);
									return;
								}

								var actual = settings.enumerable.any(undefined, function(err, value) {
									assert.equal(err, undefined);
									assert.equal(value, false);
													
									if (settings.enumerable.isAsynchronous) {
										callback(true);
									}
								});

								assert.equal(actual, !settings.enumerable.isAsynchronous ? false : undefined);

								if (!settings.enumerable.isAsynchronous) {
									actual = settings.enumerable.any();

									assert.equal(actual, false);
									callback(true);
								}
							});
						},
						'a predicate that returns true should cause the result to be true' : function(done) {
							runTest(done, function(callback, settings) {
								if (settings.result.length < 1) {
									callback(false);
									return;
								}

								var actual = settings.enumerable.any(function(v,i) { if (i === settings.result.length - 1) { return true; } }, function(err, value) {
									assert.equal(err, undefined);
									assert.equal(value, true);
													
									if (settings.enumerable.isAsynchronous) {
										callback(true);
									}
								});

								assert.equal(actual, !settings.enumerable.isAsynchronous ? true : undefined);

								if (!settings.enumerable.isAsynchronous) {
									actual = settings.enumerable.any(function(v,i) { if (i === settings.result.length - 1) { return true; } });

									assert.equal(actual, true);
									callback(true);
								}
							});
						},
						'a predicate that always returns false should cause the result to be false' : function(done) {
							runTest(done, function(callback, settings) {
								var actual = settings.enumerable.any(function(v,i) { return false; }, function(err, value) {
									assert.equal(err, undefined);
									assert.equal(value, false);
													
									if (settings.enumerable.isAsynchronous) {
										callback(true);
									}
								});

								assert.equal(actual, !settings.enumerable.isAsynchronous ? false : undefined);

								if (!settings.enumerable.isAsynchronous) {
									actual = settings.enumerable.any(function(v,i) { return false; });

									assert.equal(actual, false);
									callback(true);
								}
							});
						},
						'if no predicate is specified it should return true if the enumerable is not empty' : function(done) {
							runTest(done, function(callback, settings) {
								if (settings.result.length < 1) {
									callback(false);
									return;
								}

								var actual = settings.enumerable.any(undefined, function(err, value) {
									assert.equal(err, undefined);
									assert.equal(value, true);
													
									if (settings.enumerable.isAsynchronous) {
										callback(true);
									}
								});

								assert.equal(actual, !settings.enumerable.isAsynchronous ? true : undefined);

								if (!settings.enumerable.isAsynchronous) {
									actual = settings.enumerable.any();

									assert.equal(actual, true);
									callback(true);
								}
							});
						},
						'a predicate should be passed the current item as the last argument and the index as the second': function(done) {
							runTest(done, function(callback, settings) {
								var index  = -1;
								var actual = settings.enumerable.any(function(i,j) { index++; return !(i === settings.result[index] && j === index); }, function(err, value) {
									assert.equal(err, undefined);
									assert.equal(value, false);
													
									if (settings.enumerable.isAsynchronous) {
										callback(true);
									}
								});

								assert.equal(actual, !settings.enumerable.isAsynchronous ? false : undefined);

								if (!settings.enumerable.isAsynchronous) {
									index  = -1;
									actual = settings.enumerable.any(function(i,j) { index++; return !(i === settings.result[index] && j === index); });

									assert.equal(actual, false);
									callback(true);
								}
							});
						}
					},
					'#concat(second)': {
						'two enumerables should result in one enumerable with both values' : function(done) {
							runTest(done, function(callback, settings) {
								if (!settings.enumerable2) {
									callback(false);
									return;
								}

								var actual = settings.enumerable.concat(settings.enumerable2).toArray(function(err, arr) {
									assert.equal(err, undefined);
									assert.deepEqual(arr, settings.result.concat(settings.result2));
											
									if (settings.enumerable.isAsynchronous) {
										callback(true);
									}
								});

								assert.deepEqual(actual, !settings.enumerable.isAsynchronous ? settings.result.concat(settings.result2) : undefined);

								if (!settings.enumerable.isAsynchronous && !settings.enumerable2.isAsynchronous) {
									actual = settings.enumerable.concat(settings.enumerable2).toArray();

									assert.deepEqual(actual, settings.result.concat(settings.result2));
									callback(true);
								}
							});
						},
						'passing an empty enumerable should result in only the values in the other enumerable' : function(done) {
							runTest(done, function(callback, settings) {
								if (!settings.enumerable2) {
									callback(false);
									return;
								}

								var actual = settings.enumerable.concat(settings.enumerable2.skip(settings.result2.length)).toArray(function(err, arr) {
									assert.equal(err, undefined);
									assert.deepEqual(arr, settings.result);
											
									if (settings.enumerable.isAsynchronous) {
										callback(true);
									}
								});

								assert.deepEqual(actual, !settings.enumerable.isAsynchronous ? settings.result : undefined);

								if (!settings.enumerable.isAsynchronous && !settings.enumerable2.isAsynchronous) {
									actual = settings.enumerable.concat(settings.enumerable2.skip(settings.result2.length)).toArray();

									assert.deepEqual(actual, settings.result);
									callback(true);
								}
							});
						}
					},
					'#insert(index, second)': {
						'inserting the second array at the beginning of the first should result in a reverse concat' : function(done) {
							runTest(done, function(callback, settings) {
								if (!settings.enumerable2) {
									callback(false);
									return;
								}

								var actual = settings.enumerable.insert(0, settings.enumerable2).toArray(function(err, arr) {
									assert.equal(err, undefined);
									assert.deepEqual(arr, settings.result2.concat(settings.result));
											
									if (settings.enumerable.isAsynchronous) {
										callback(true);
									}
								});

								assert.deepEqual(actual, !settings.enumerable.isAsynchronous ? settings.result2.concat(settings.result) : undefined);

								if (!settings.enumerable.isAsynchronous && !settings.enumerable2.isAsynchronous) {
									actual = settings.enumerable.insert(0, settings.enumerable2).toArray();

									assert.deepEqual(actual, settings.result2.concat(settings.result));
									callback(true);
								}
							});
						},
						'inserting the second array at the ending of the first should result in a concat' : function(done) {
							runTest(done, function(callback, settings) {
								if (!settings.enumerable2) {
									callback(false);
									return;
								}

								var actual = settings.enumerable.insert(settings.result.length, settings.enumerable2).toArray(function(err, arr) {
									assert.equal(err, undefined);
									assert.deepEqual(arr, settings.result.concat(settings.result2));
											
									if (settings.enumerable.isAsynchronous) {
										callback(true);
									}
								});

								assert.deepEqual(actual, !settings.enumerable.isAsynchronous ? settings.result.concat(settings.result2) : undefined);

								if (!settings.enumerable.isAsynchronous && !settings.enumerable2.isAsynchronous) {
									actual = settings.enumerable.insert(settings.result.length, settings.enumerable2).toArray();

									assert.deepEqual(actual, settings.result.concat(settings.result2));
									callback(true);
								}
							});
						},
						'inserting the second array in the middle should mix the two enumerables' : function(done) {
							runTest(done, function(callback, settings) {
								if (!settings.enumerable2) {
									callback(false);
									return;
								}

								var index  = Math.floor(settings.result.length / 2);
								var result = settings.result.slice(0, index).concat(settings.result2).concat(settings.result.slice(index));
								var actual = settings.enumerable.insert(index, settings.enumerable2).toArray(function(err, arr) {
									assert.equal(err, undefined);
									assert.deepEqual(arr, result);
											
									if (settings.enumerable.isAsynchronous) {
										callback(true);
									}
								});

								assert.deepEqual(actual, !settings.enumerable.isAsynchronous ? result : undefined);

								if (!settings.enumerable.isAsynchronous && !settings.enumerable2.isAsynchronous) {
									actual = settings.enumerable.insert(index, settings.enumerable2).toArray();

									assert.deepEqual(actual, result);
									callback(true);
								}
							});
						}
					},
					'#alternate(value)': {
						'an empty enumerable should result in an empty enumerable' : function(done) {
							runTest(done, function(callback, settings) {
								if (settings.result.length > 0) {
									callback(false);
									return;
								}

								var actual = settings.enumerable.alternate(2).toArray(function(err, arr) {
									assert.equal(err, undefined);
									assert.deepEqual(arr, []);
											
									if (settings.enumerable.isAsynchronous) {
										callback(true);
									}
								});

								assert.deepEqual(actual, !settings.enumerable.isAsynchronous ? [] : undefined);

								if (!settings.enumerable.isAsynchronous) {
									actual = settings.enumerable.alternate(2).toArray();

									assert.deepEqual(actual, []);
									callback(true);
								}
							});
						},
						'the resulting enumerable should have the value specified at the odd indexes' : function(done) {
							runTest(done, function(callback, settings) {
								var result = [];
										
								for (var i = 0; i < settings.result.length; i++) {
									result.push(settings.result[i]);

									if (i < settings.result.length - 1) {
										result.push(2);
									}
								}

								var actual = settings.enumerable.alternate(2).toArray(function(err, arr) {
									assert.equal(err, undefined);
									assert.deepEqual(arr, result);
											
									if (settings.enumerable.isAsynchronous) {
										callback(true);
									}
								});

								assert.deepEqual(actual, !settings.enumerable.isAsynchronous ? result : undefined);

								if (!settings.enumerable.isAsynchronous) {
									actual = settings.enumerable.alternate(2).toArray();

									assert.deepEqual(actual, result);
									callback(true);
								}
							});
						},
						'an enumerable with one element should result in an enumerable with a single element' : function(done) {
							runTest(done, function(callback, settings) {
								if (settings.result.length !== 1) {
									callback(false);
									return;
								}

								var actual = settings.enumerable.alternate(2).toArray(function(err, arr) {
									assert.equal(err, undefined);
									assert.deepEqual(arr, settings.result.slice(0,1));
											
									if (settings.enumerable.isAsynchronous) {
										callback(true);
									}
								});

								assert.deepEqual(actual, !settings.enumerable.isAsynchronous ? settings.result.slice(0,1) : undefined);

								if (!settings.enumerable.isAsynchronous) {
									actual = settings.enumerable.alternate(2).toArray();

									assert.deepEqual(actual, settings.result.slice(0,1));
									callback(true);
								}
							});
						}
					},
					'#contains(value, [compareSelector<element>], [doneCallback<err, result>])': {
						'an empty enumerable should always return false' : function(done) {
							runTest(done, function(callback, settings) {
								if (settings.result.length > 0) {
									callback(false);
									return;
								}

								var item = 2;

								if (settings.result.length > 0) {
									item = settings.result[Math.floor(settings.result.length / 2)];
								}

								var actual = settings.enumerable.contains(item, undefined, function(err, value) {
									assert.equal(err, undefined);
									assert.equal(value, false);
											
									if (settings.enumerable.isAsynchronous) {
										callback(true);
									}
								});

								assert.equal(actual, !settings.enumerable.isAsynchronous ? false : undefined);

								if (!settings.enumerable.isAsynchronous) {
									actual = settings.enumerable.contains(item);

									assert.equal(actual, false);
									callback(true);
								}
							});
						},
						'it should return true when called with an item in the enumerable' : function(done) {
							runTest(done, function(callback, settings) {
								if (settings.result.length < 1) {
									callback(false);
									return;
								}

								var item = 2;

								if (settings.result.length > 0) {
									item = settings.result[Math.floor(settings.result.length / 2)];
									item = settings.useKey ? item.key : item;
								}

								var actual = settings.enumerable.contains(item, undefined, function(err, value) {
									assert.equal(err, undefined);
									assert.equal(value, true);
											
									if (settings.enumerable.isAsynchronous) {
										callback(true);
									}
								});

								assert.equal(actual, !settings.enumerable.isAsynchronous ? true : undefined);

								if (!settings.enumerable.isAsynchronous) {
									actual = settings.enumerable.contains(item);

									assert.equal(actual, true);
									callback(true);
								}
							});
						},
						'it should return true when called with an item in the enumerable (with selector)' : function(done) {
							runTest(done, function(callback, settings) {
								if (settings.result.length < 1) {
									callback(false);
									return;
								}

								var item   = 2;

								item = settings.result[Math.floor(settings.result.length / 2)];
								item = settings.useKey ? item.key : item;

								var actual = settings.enumerable.select('{test:$}').contains(item, settings.useKey ? '$.test.key' : '$.test', function(err, value) {
									assert.equal(err, undefined);
									assert.equal(value, true);
											
									if (settings.enumerable.isAsynchronous) {
										callback(true);
									}
								});

								assert.equal(actual, !settings.enumerable.isAsynchronous ? true : undefined);

								if (!settings.enumerable.isAsynchronous) {
									actual = settings.enumerable.select('{test:$}').contains(item, settings.useKey ? '$.test.key' : '$.test');

									assert.equal(actual, true);
									callback(true);
								}
							});
						},
						'it should return false when called with an item that is not in the enumerable' : function(done) {
							runTest(done, function(callback, settings) {
								var item = { dummy: true };
								var actual = settings.enumerable.contains(item, undefined, function(err, value) {
									assert.equal(err, undefined);
									assert.equal(value, false);
											
									if (settings.enumerable.isAsynchronous) {
										callback(true);
									}
								});

								assert.equal(actual, !settings.enumerable.isAsynchronous ? false : undefined);

								if (!settings.enumerable.isAsynchronous) {
									actual = settings.enumerable.contains(item);

									assert.equal(actual, false);
									callback(true);
								}
							});
						},
						'it should return false when called with an item that is not in the enumerable (with selector)' : function(done) {
							runTest(done, function(callback, settings) {
								var item = { dummy: true };
								var actual = settings.enumerable.select('{test:$}').contains(item, '$.test', function(err, value) {
									assert.equal(err, undefined);
									assert.equal(value, false);
											
									if (settings.enumerable.isAsynchronous) {
										callback(true);
									}
								});

								assert.equal(actual, !settings.enumerable.isAsynchronous ? false : undefined);

								if (!settings.enumerable.isAsynchronous) {
									actual = settings.enumerable.select('{test:$}').contains(item, '$.test');

									assert.equal(actual, false);
									callback(true);
								}
							});
						}
					},
					'#defaultIfEmpty(defaultValue)': {
						'it should return the default value if the enumerable is empty' : function(done) {
							runTest(done, function(callback, settings) {
								if (settings.result.length > 0) {
									callback(false);
									return;
								}

								var actual = settings.enumerable.defaultIfEmpty(4).toArray(function(err, arr) {
									assert.equal(err, undefined);
									assert.deepEqual(arr, [4]);
											
									if (settings.enumerable.isAsynchronous) {
										callback(true);
									}
								});

								assert.deepEqual(actual, !settings.enumerable.isAsynchronous ? [4] : undefined);

								if (!settings.enumerable.isAsynchronous) {
									actual = settings.enumerable.defaultIfEmpty(4).toArray();

									assert.deepEqual(actual, [4]);
									callback(true);
								}
							});
						},
						'it should return all the items in the enumerable if it is not empty' : function(done) {
							runTest(done, function(callback, settings) {
								if (settings.result.length < 1) {
									callback(false);
									return;
								}

								var actual = settings.enumerable.defaultIfEmpty(4).toArray(function(err, arr) {
									assert.equal(err, undefined);
									assert.deepEqual(arr, settings.result);
											
									if (settings.enumerable.isAsynchronous) {
										callback(true);
									}
								});

								assert.deepEqual(actual, !settings.enumerable.isAsynchronous ? settings.result : undefined);

								if (!settings.enumerable.isAsynchronous) {
									actual = settings.enumerable.defaultIfEmpty(4).toArray();

									assert.deepEqual(actual, settings.result);
									callback(true);
								}
							});
						}
					},
					'#distinct[compareSelector<element>]': {
						'there should be no duplicates in the resulting enumerable' : function(done) {
							runTest(done, function(callback, settings) {
								var unique = getUnique(settings.result);
								var actual = settings.enumerable.distinct().toArray(function(err, arr) {
									assert.equal(err, undefined);
									assert.deepEqual(arr, unique);
											
									if (settings.enumerable.isAsynchronous) {
										callback(true);
									}
								});

								assert.deepEqual(actual, !settings.enumerable.isAsynchronous ? unique : undefined);

								if (!settings.enumerable.isAsynchronous) {
									actual = settings.enumerable.distinct().toArray();

									assert.deepEqual(actual, unique);
									callback(true);
								}
							});
						},
						'there should be no duplicates in the resulting enumerable (with selector)' : function(done) {
// BAD TEST: You must call distinct directly off of the enumerable in order to test it properly
							runTest(done, function(callback, settings) {
								var unique;
								var result = [];
								var actual = settings.enumerable.select('{test:$}').Do(function(v) { result.push(v); }).distinct().toArray(function(err, arr) {
									unique = getUnique(result);

									assert.equal(err, undefined);
									assert.deepEqual(arr, unique);
											
									if (settings.enumerable.isAsynchronous) {
										callback(true);
									}
								});

								assert.deepEqual(actual, !settings.enumerable.isAsynchronous ? unique : undefined);

								if (!settings.enumerable.isAsynchronous) {
									actual = settings.enumerable.select('{test:$}').distinct().toArray();

									assert.deepEqual(actual, unique);
									callback(true);
								}
							});
						}
					},
					'#except(second, [compareSelector<element>])': {
						'the resulting enumerable should not contain any elements that are in the supplied enumerable' : function(done) {
							runTest(done, function(callback, settings) {
								if (!settings.result2) {
									callback(false);
									return;
								}

								var result = excludeItems(settings.result, settings.result2);
								var actual = settings.enumerable.except(settings.enumerable2).toArray(function(err, arr) {
									assert.equal(err, undefined);
									assert.deepEqual(arr, result);
											
									if (settings.enumerable.isAsynchronous) {
										callback(true);
									}
								});

								assert.deepEqual(actual, !settings.enumerable.isAsynchronous ? result : undefined);

								if (!settings.enumerable.isAsynchronous) {
									actual = settings.enumerable.except(settings.enumerable2).toArray();

									assert.deepEqual(actual, result);
									callback(true);
								}
							});
						},
						'the resulting enumerable should not contain any elements that are in the supplied enumerable (with selector)' : function(done) {
							runTest(done, function(callback, settings) {
								if (!settings.result2) {
									callback(false);
									return;
								}

								var result = [];
								var actual = settings.enumerable.select('{test:$}')
												.Do(function(v) { if (settings.result2.indexOf(v.test ) < 0) { result.push(v); } })
												.except(settings.enumerable2.select('{test:$}'), '$.test')
												.toArray(function(err, arr) {
									assert.equal(err, undefined);
									assert.deepEqual(arr, result);

									if (settings.enumerable.isAsynchronous) {
										callback(true);
									}
								});

								assert.deepEqual(actual, !settings.enumerable.isAsynchronous ? result : undefined);

								if (!settings.enumerable.isAsynchronous) {
									result = [];
									actual = settings.enumerable.select('{test:$}')
												.Do(function(v) { if (settings.result2.indexOf(v.test ) < 0) { result.push(v); } })
												.except(settings.enumerable2.select('{test:$}'), '$.test')
												.toArray();

									assert.deepEqual(actual, result);
									callback(true);
								}
							});
						}
					},
					'#intersect(second, [compareSelector<element>])': {
						'the resulting enumerable should only contain elements in both enumerables' : function(done) {
							runTest(done, function(callback, settings) {
								if (!settings.result2) {
									callback(true);
									return;
								}

								var result = [];
								var actual = settings.enumerable
												.Do(function(v) { if (settings.result2.indexOf(v) > -1 && result.indexOf(v) < 0) { result.push(v); } })
												.intersect(settings.enumerable2).toArray(function(err, arr) {
									assert.equal(err, undefined);
									assert.deepEqual(arr, result);
											
									if (settings.enumerable.isAsynchronous) {
										callback(true);
									}
								});

								assert.deepEqual(actual, !settings.enumerable.isAsynchronous ? result : undefined);

								if (!settings.enumerable.isAsynchronous) {
									actual = settings.enumerable.intersect(settings.enumerable2).toArray();

									assert.deepEqual(actual, result);
									callback(true);
								}
							});
						},
						'the resulting enumerable should only contain elements in both enumerables (with selector)' : function(done) {
							runTest(done, function(callback, settings) {
								if (!settings.result2) {
									callback(false);
									return;
								}

								var result = [];
								var unique = [];
								var actual = settings.enumerable.select('{test:$}')
												.Do(function(v) { if (settings.result2.indexOf(v.test ) > -1 && unique.indexOf(v.test) < 0) { unique.push(v.test); result.push(v); } })
												.intersect(settings.enumerable2.select('{test:$}'), '$.test')
												.toArray(function(err, arr) {
									assert.equal(err, undefined);
									assert.deepEqual(arr, result);

									if (settings.enumerable.isAsynchronous) {
										callback(true);
									}
								});

								assert.deepEqual(actual, !settings.enumerable.isAsynchronous ? result : undefined);

								if (!settings.enumerable.isAsynchronous) {
									result = [];
									unique = [];
									actual = settings.enumerable.select('{test:$}')
												.Do(function(v) { if (settings.result2.indexOf(v.test ) > -1 && unique.indexOf(v.test) < 0) { unique.push(v.test); result.push(v); } })
												.intersect(settings.enumerable2.select('{test:$}'), '$.test')
												.toArray();

									assert.deepEqual(actual, result);
									callback(true);
								}
							});
						}
					},
					'#sequenceEqual(second, [compareSelector<element>], [doneCallback])': {
						'two empty enumerables should return true' : function(done) {
							runTest(done, function(callback, settings) {
								if (!settings.result2 || settings.result.length > 0) {
									callback(false);
									return;
								}

								var actual = settings.enumerable.sequenceEqual(settings.enumerable2.skip(settings.result2.length), undefined, function(err, value) {
									assert.equal(err, undefined);
									assert.equal(value, true);
											
									if (settings.enumerable.isAsynchronous) {
										callback(true);
									}
								});

								assert.equal(actual, !settings.enumerable.isAsynchronous ? true : undefined);

								if (!settings.enumerable.isAsynchronous) {
									actual = settings.enumerable.sequenceEqual(settings.enumerable2.skip(settings.result2.length));

									assert.equal(actual, true);
									callback(true);
								}
							});
						},
						'if the two enumerables are the same then it should true; otherwise false' : function(done) {
							var equalFound = false;

							runTest(done, function(callback, settings) {
								if (!settings.result2) {
									callback(false);
									return;
								}

								var result = settings.result.length === settings.result2.length;

								if (result) {
									for (var i = 0; i < settings.result.length; i++) {
										if (settings.result[i] !== settings.result2[i]) {
											result = false;
											break;
										}
									}
								}

								if (result) {
									equalFound = true;
								}

								var actual = settings.enumerable.sequenceEqual(settings.enumerable2, undefined, function(err, value) {
									assert.equal(err, undefined);
									assert.equal(value, result);
											
									if (settings.enumerable.isAsynchronous) {
										callback(true);
									}
								});

								assert.equal(actual, !settings.enumerable.isAsynchronous ? result : undefined);

								if (!settings.enumerable.isAsynchronous) {
									actual = settings.enumerable.sequenceEqual(settings.enumerable2);

									assert.equal(actual, result);
									callback(true);
								}
							});

							if (!equalFound) {
								assert(false, 'No valid true test case was supplied.');
							}								},
						'if the two enumerables are the same then it should true; otherwise false (with selector)' : function(done) {
// BAD TEST: sequenceEqual must be called directly off of enumerable in order to test it proprely.
							var equalFound = false;

							runTest(done, function(callback, settings) {
								if (!settings.result2) {
									callback(false);
									return;
								}

								var result = settings.result.length === settings.result2.length;

								if (result) {
									for (var i = 0; i < settings.result.length; i++) {
										if (settings.result[i] !== settings.result2[i]) {
											result = false;
											break;
										}
									}
								}

								if (result) {
									equalFound = true;
								}

								var actual = settings.enumerable.select('{test:$}').sequenceEqual(settings.enumerable2.select('{test:$}'), '$.test', function(err, value) {
									assert.equal(err, undefined);
									assert.equal(value, result);
											
									if (settings.enumerable.isAsynchronous) {
										callback(true);
									}
								});

								assert.equal(actual, !settings.enumerable.isAsynchronous ? result : undefined);

								if (!settings.enumerable.isAsynchronous) {
									actual = settings.enumerable.select('{test:$}').sequenceEqual(settings.enumerable2.select('{test:$}'), '$.test');

									assert.equal(actual, result);
									callback(true);
								}
							});

							if (!equalFound) {
								assert(false, 'No valid true test case was supplied.');
							}
						}
					},
					'#union(second, [compareSelector<element>])': {
						'the resulting enumerable should be the unique values of both enumerables' : function(done) {
							runTest(done, function(callback, settings) {
								if (!settings.result2) {
									callback(false);
									return;
								}

								var result = getUnique(settings.result.concat(settings.result2));
								var actual = settings.enumerable.union(settings.enumerable2).toArray(function(err, arr) {
									assert.equal(err, undefined);
									assert.deepEqual(arr, result);
											
									if (settings.enumerable.isAsynchronous) {
										callback(true);
									}
								});

								assert.deepEqual(actual, !settings.enumerable.isAsynchronous ? result : undefined);

								if (!settings.enumerable.isAsynchronous) {
									actual = settings.enumerable.union(settings.enumerable2).toArray();

									assert.deepEqual(actual, result);
									callback(true);
								}
							});
						},
						'the resulting enumerable should be the unique values of both enumerables (with selector)' : function(done) {
// BAD TEST: union must be called directly off of enumerable in order to test it properly.
							runTest(done, function(callback, settings) {
								if (!settings.result2) {
									callback(false);
									return;
								}

								var unique = [];
								var result = [];
								var actual = settings.enumerable.select('{test:$}')
												.Do(function(v) { if (unique.indexOf(v.test) < 0) { unique.push(v.test); result.push(v); } })
												.union(
													settings.enumerable2.select('{test:$}'), '$.test')
														.Do(function(v) { if (unique.indexOf(v.test) < 0) { unique.push(v.test); result.push(v); } })
												.toArray(function(err, arr) {
									assert.equal(err, undefined);
									assert.deepEqual(arr, result);
											
									if (settings.enumerable.isAsynchronous) {
										callback(true);
									}
								});

								assert.deepEqual(actual, !settings.enumerable.isAsynchronous ? result : undefined);

								if (!settings.enumerable.isAsynchronous) {
									unique = [];
									result = [];
									actual = settings.enumerable.select('{test:$}')
												.Do(function(v) { if (unique.indexOf(v.test) < 0) { unique.push(v.test); result.push(v); } })
												.union(
													settings.enumerable2.select('{test:$}'), '$.test')
														.Do(function(v) { if (unique.indexOf(v.test) < 0) { unique.push(v.test); result.push(v); } })
														.toArray();

									assert.deepEqual(actual, result);
									callback(true);
								}
							});
						}
					}
				}
			}
		);

		return tests;
	}

	return LinqTests;
})();

// Hook into commonJS module systems
if (typeof module !== 'undefined' && "exports" in module) {
  module.exports = LinqTests;
}